1

I'm pretty new to JS and first time on here.

How do you return an object within an array.reduce?

So for example: I want to find the lowest low for a given day's dataset (I'm using a currency API as an interesting data-set to play with). The data I get back is an array of objects. So I was using a reduce to iterate over, day-by-day and find the lowest low.

const lowestLow = historialCandlesFloatData.reduce((lowestLow,currentDay) => {
        if(currentDay.l < lowestLow.l){
             return lowestLow = currentDay;
        }else return lowestLow;   
},);

That works fine, but I'm also going to need the index of the object returned as the lowest. Now I know you can use index as part of reduce, and I can console.log what index the lowest low is, but I can't figure out how to return it along with the low as an object?

I could just use a forEach, which seems to work fine, but this doesn't seem the best way to do it and id really like to know!

const findLow = ()=>{
    let currentDayLow = 2;
    let dataIndex = 0;
    let datavalue = 0;

        historialCandlesFloatData.forEach((day)=>{
            if(day.l < currentDayLow){
                currentDayLow = day.l;
                datavalue = dataIndex;   
            }
            dataIndex ++;   
        });
    return {
        lowestLow: currentDayLow,
        indexValue: datavalue,
    }
}
1
  • 1
    Yes sorry I spotted that just after posting, and have now fixed, but as you can see not the most elegant way to do it. Your reduce solution is what I was hoping to arrive at. Commented Jul 31, 2022 at 11:31

3 Answers 3

4

If you want anything else/more than a value from the array, you'll benefit from using the second argument of reduce: the initial value of the accumulator. In this case the accumulator would be the currently "best" index, and so that accumulator initial value could be 0.

NB: instead of if else you could use the conditional operator.

const lowestIdx = historialCandlesFloatData.reduce((lowestIdx, currentDay, i) => {
    return currentDay.l < historialCandlesFloatData[lowestIdx].l
             ? i
             : lowestIdx;   
}, 0); // Initial index

And then with that index you can retrieve the corresponding object

const lowest = historialCandlesFloatData[lowestIdx];
Sign up to request clarification or add additional context in comments.

4 Comments

you can't return when looking for lowest value
Ah nice... yes that makes sense, much more elegant. (I also just realised by forEach way of tracking the index does not work anyway). Thank you @trincot
@YasserCHENIK, I think you mistake the return statement for something else. This return does not interrupt the loop.
@YasserCHENIK - The return just returns from the callback, whereupon reduce calls it again with the next element.
2

To do that with reduce, you'll need to make the "accumulator" you pass around an object with the value and index on it. (Er, or just return the index then use that to get the element, as trincot showed.) For instance:

const lowestLow = historialCandlesFloatData.reduce((lowestLow, currentDay, index) => {
    return !lowestLow || currentDay.l < lowestLow.entry.l ? { entry: currentDay, index } : lowestLow;
}, null);

Live Example:

const historialCandlesFloatData = [{ l: 4 }, { l: 1 }, { l: 3 }, { l: 7 }];

const lowestLow = historialCandlesFloatData.reduce((lowestLow, currentDay, index) => {
    return !lowestLow || currentDay.l < lowestLow.entry.l ? { entry: currentDay, index } : lowestLow;
}, null);

console.log(lowestLow);

Alternatively, you could make the accumulator an augmented version of the objects from the array by adding the index to it:

const lowestLow = historialCandlesFloatData.reduce((lowestLow, currentDay, index) => {
    return !lowestLow || currentDay.l < lowestLow.l ? { ...currentDay, index } : lowestLow;
}, null);

Live Example:

const historialCandlesFloatData = [{ l: 4 }, { l: 1 }, { l: 3 }, { l: 7 }];

const lowestLow = historialCandlesFloatData.reduce((lowestLow, currentDay, index) => {
    return !lowestLow || currentDay.l < lowestLow.l ? { ...currentDay, index } : lowestLow;
}, null);

console.log(lowestLow);


But I'm not a fan of using reduce with ad hoc functions, not least because it's so easy to trip over it by forgetting to provide a seed value (when necessary), forgetting to do a return on all code paths, etc. Unless you're doing functional programming with predefined, reusable reducer functions, I much prefer simple loops:

let lowestLow = null;
let lowestIndex;
for (let index = 0; index < historialCandlesFloatData.length; ++index) {
    const entry = historialCandlesFloatData[index];
    if (!lowestLow || entry.l < lowestLow.l) {
        lowestLow = entry;
        lowestIndex = index;
    }
}

Live Example:

const historialCandlesFloatData = [{ l: 4 }, { l: 1 }, { l: 3 }, { l: 7 }];

let lowestLow = null;
let lowestIndex;
for (let index = 0; index < historialCandlesFloatData.length; ++index) {
    const entry = historialCandlesFloatData[index];
    if (!lowestLow || entry.l < lowestLow.l) {
        lowestLow = entry;
        lowestIndex = index;
    }
}

console.log(lowestLow);

Yes, in this case it's more code, but you may find it clearer and/or less error-prone, especially if you're just starting out.

1 Comment

Thansk for your help, agree normal loops are certainly more what im used to but just trying to get used to some of the various array functions.
1

Why don't you add a new property to the object using JavaScript's spread operator ...? For example, like this:

const lowestLow = historialCandlesFloatData.reduce((lowestLow, currentDay, idx) => {
        if(currentDay.l < lowestLow.l){
             return {
                 ...currentDay,
                 idx: idx,
             };
        } else {
             return lowestLow;
        }  
},
// Initial value
{ l: historialCandlesFloatData[0].l + 1 }
);

// Now you can access the index like so
const index = lowestLow.idx;

2 Comments

@trincot correct, I had just realised this. Your solution is a better one, anyway, but I will update to fix this.
OK, good now ;-) +1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.