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 itterate 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 cant 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 doesnt 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,
}
}
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];
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.
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;