curr is undefined in reduce on array of objects - javascript

As an input, I have an array of objects , each one of these objects has a number of properties,
I would like to have an output that is also an array of objects with the same properties unchanged except for one property that I like to be changed into an accumulative value.
input example :
let input_array = [{ a:'a',b:'b',c: 1},{a:'d',b:'e',c:2},{a:'g',b:'h',c: 3}];
output example
let output_array = [{ a:'a',b:'b',c: 1},{a:'d',b:'e',c:3},{a:'g',b:'h',c: 6}];
Here 's what I tried :
let output_array = [];
input_array.reduce((acc, curr) => {
output_array.push({
a: curr.a,
b: curr.b,
c : acc.c + curr.c
})
},0);
I keep getting c NaN and acc undefined .

In this case I'd prefer to .map one array to the other, and keep a variable with the accumulated c:
const input = [{ a:'a',b:'b',c: 1},{a:'d',b:'e',c:2},{a:'g',b:'h',c: 3}];
const output = input.map(((acc) => ({ c, ...rest }) => ({ ...rest, c: acc += c }))(0));
console.log(output);
For sure your solution also works if you actually return the new accumulator from the reducer:
const input = [{ a:'a',b:'b',c: 1},{a:'d',b:'e',c:2},{a:'g',b:'h',c: 3}];
const output = [];
input.reduce((acc, curr) => {
acc = acc + curr.c;
output.push({ ...curr, c: acc });
return acc;
},0);
console.log(output);

Related

How can I do total count of values accordingly selected feild in array

I have a array of objects, for exemple:
let arr = [
{title:apple,quantity:2},
{title:banana,quantity:3},
{title:apple,quantity:5},
{title:banana,quantity:7}
];
array containe many same objects, and i want recived array with uniqe object :
let result = [
{title:apple,quantity:7},
{title:banana,quantity:10}
]
How can I do this?
You can iterate over your array and filter out all the object with same title. Then use reduce to add all the quantity and return a new object. Code is below,
let newArr = [];
arr.forEach((currentObj) => {
const alreadyExists = newArr.findIndex(item => currentObj.title === item.title) > -1;
if(!alreadyExists) {
const filtered = arr.filter(item => item.title === currentObj.title);
const newObject = filtered.reduce((acc, curr) => { return {...acc, quantity: acc.quantity += curr.quantity}}, {...currentObj, quantity: 0})
newArr.push(newObject);
}
})
console.log(newArr);
This is done on a phone so may have some typos but the gist is there:
const resultObj = arr.reduce((acc,curr) =>{
acc[curr.title] = acc[curr.title]== undefined? curr.quantity: acc[curr.title] + curr.quantity
return acc
},{})
const resultArr = Object.entries(resultObj).map([key,value]=>({title:key,quantity:value}))
You could do that in "one line" using arrow function expressions but it won't be very readable unless you know what's happening inside:
let arr = [
{title: "apple",quantity:2},
{title: "banana",quantity:3},
{title: "apple",quantity:5},
{title: "banana",quantity:7}
];
let newArr = [...arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
].map(([title, quantity]) => ({title, quantity}));
console.log(newArr);
So basically the first part is the reduce method:
arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
That will returns a Map object, where each title is a key (e.g. "apple") and the quantity is the value of the key.
At this point you have to convert the Map object into an array again, and you do it using the spread syntax.
After you got an array back, you will have it in the following form:
[["apple", 7], ["banana", 10]]
But that is not what you want yet, not in this form, so you have to convert it using the array's map method:
<array>.map(([title, quantity]) => ({title, quantity}))
To keep it concise it uses the destructuring assignment

How to concert [{ id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}] into [{id:1, var1:val1, var2:val2, varX1:time1, varX2:time2, varX3:time3}]

I am working on a data format transformation which showed on the title, but I don't know how to figure out. I have tried to write the blow code to add the variable name for second dimensional array:
const data = [
{ id: 1, var1: 'val1', var2: 'val2', varX: ['time1', 'time2', 'time3'] },
{ id: 2, var1: 'val2', var2: 'val3', varX: ['time4', 'time5', 'time6'] },
];
const test = data.map((o) => o.varX);
for (i = 0; i < test.length; i++) {
const test2 = test[i].reduce((res, cur, idx) => {
res[`varX${idx}`] = cur;
return res;
}, {});
console.log(test2);
}
but what I expected result should be:
[{id:1, var1:val1, var2:val2, varX1:time1, varX2:time2, varX3:time3},{id:2, var1:val2, var2:val3, varX1:time4, varX2:time5, varX3:time6}]
Could anyone can guide me how to convert the data?
The issue with your code is that you're extracgint the properties that are not varX from the data by mapping only the values that have a varX. Then you do a good job at reducing them, but then you would have to merge the "left over" properties into the new object, it sounds a bit cumbersome to me. Instead, you could do something like the following:
const data = [
{ id: 1, var1: "val1", var2: "val2", varX: ["time1", "time2", "time3"] },
{ id: 2, var1: "val2", var2: "val3", varX: ["time4", "time5", "time6"] },
];
const test = data.map((o) => {
return Object.entries(o).reduce((p, [k, v]) => {
if (k === "varX") {
for (let index = 0; index < v.length; index++) {
p[`varX${index + 1}`] = v[index];
}
} else {
p[k] = v;
}
return p;
}, {});
});
console.log(test);
First, you map the data objects, then for each object you reduce their key/value pairs and check if the property is varX, if so, then you iterate through the array and assign to the new object the varX${index + 1} since you want the first property to be varX1, otherwise you just keep the same key/value pair.
You need create objects in reducemethod instead array, after replace varX to this new object by second map() method
const data = [{'id':1, 'var1':'val1', 'var2':'val2', 'varX':['time1', 'time2', 'time3']},
{'id':2, 'var1':'val2', 'var2':'val3', 'varX':['time4', 'time5', 'time6']}]
const test = data.map(item => item.varX);
const test2 = {}
for (i = 0; i < test.length; i++) {
test2[i] = test[i].reduce((accum, item, index) => {
return { ...accum, [`varX${index}`]: item};
}, {});
}
const dataX = data.map((item, index) => {
delete item.varX
return { ...item, ...test2[index] }
});
console.log(dataX)
Because of the first map in test, you are operating only on values of varX. You can just add another operation to merge the original data[i] objects with your new reduced varX objects.
const data = [
{ id: 1, var1: 'val1', var2: 'val2', varX: ['time1', 'time2', 'time3'] },
{ id: 2, var1: 'val2', var2: 'val3', varX: ['time4', 'time5', 'time6'] },
];
const test = data.map((o) => o.varX);
for (i = 0; i < test.length; i++) {
const test2 = test[i].reduce((res, cur, idx) => {
res[`varX${idx}`] = cur;
return res;
}, {});
// exclude varX and merge your new varX${i} back into data[i]
const {varX, ...test3} = Object.assign(data[i], test2);
console.log(test3);
}
Transform in-place code. I've deliberately avoided reduce, because the syntax becomes less readable and it adds performance overhead. The downside is that this code mutates the original data (transforms the data in-place). Can be easily mitigated by creating copies of objects val while mapping.
[1,2,3,4,5,6].forEach( i => eval(`time${i} = "time${i}";val${i} = "val${i}"`) );
const data = [{id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}, {id:2, var1:val2, var2:val3, varX:[time4, time5, time6]}]
data.forEach((val, i)=>{
val.varX.forEach( (X,i) => val[`varX${i}`]=X );
delete val.varX;
console.log(val);
return val;
});
console.log(data);
I've created a version below using reduce and spread syntax. You can look at the spread syntax as simply an operation to copy properties to the new object being generated. I've used destructuring syntax to isolate varX from the rest of the properties and put them in varX and noVarX.
This also has the advantage of deep copying except for the final outside referenced objects in data.
[1,2,3,4,5,6].forEach( i => eval(`time${i} = "time${i}";val${i} = "val${i}"`) );
const data = [{id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}, {id:2, var1:val2, var2:val3, varX:[time4, time5, time6]}]
const expanded = data.map(val => {
const {varX, ...noVarX} = val;
return {
...noVarX,
...varX.reduce( (res, cur, idx) =>
({ ...res, [`varX${idx}`]: cur }), {})
};
});
console.log(expanded);

Two object arrays: merge with same key

I have two object arrays. I want to merge with key with value
var a = [{"fit":["34","32","30","28"],"size":["x"]}]
var b = [{"size":["s","m","xl"],"fit":["36"]}]
Expected Output should be
Obj=[{"fit":["34","32","30","28","36"],"size":["x,"s","m","xl"]}]
My Code is
let arr3 = [];
b.forEach((itm, i) => {
arr3.push(Object.assign({}, itm, a[i]));
});
alert(JSON.stringify(arr3))
it gives [{"size":["x"],"fit":["34","32","30","28"]}] which wrong.
Use Array.reduce().
// Combine into single array (spread operator makes this nice)
const myArray = [...a, ...b];
// "reduce" values in array down to a single object
const reducedArray = myArray.reduce((acc, val) => {
return [{fit: [...acc.fit, ...val.fit], size: [...acc.size, ...val.size]}];
});
Edit: if you want the reducer to merge objects regardless of what keys and fields it has then you can do by iterating over the keys of the objects and merging them dynamically:
const reducedArray = myArray.reduce((acc, val) => {
const returnObject = {};
for (const eaKey in acc) {
returnObject[eaKey] = [...acc[eaKey], ...val[eaKey]];
}
return [returnObject];
});
If the fields of the objects aren't guaranteed keys then you will need to get even more dynamic in detecting the type of merge and how to do it, but it's possible and I will leave that as an exercise for you to figure out. :)
Note that if there are duplicate values in each of the "fit" and "size" arrays, they will not be deduplicated. You'd have to do that manually as a separate step either with extra logic in the reduce function or afterwards.
combine a and b in a single array then reduce it starting with an array having an object with empty fit and size arrays:
var a = [{ fit: ["34", "32", "30", "28"], size: ["x"] }];
var b = [{ size: ["s", "m", "xl"], fit: ["36"] }];
var obj = [...a, ...b].reduce(
(acc, curr) => {
Object.keys(curr).forEach(k => {
acc[0][k] = [...new Set([...(acc[0][k] || []), ...curr[k]])];
});
return acc;
},
[{}]
);
console.log(obj);
You can create a combine function that takes fit and size from any two objects and merges them.
Use it as a reducer to combine everything.
let combine = ({fit, size}, {fit: fit2, size: size2}) =>
({ fit: [...fit, ...fit2], size: [...size, ...size2] });
let result = [...a, ...b].reduce(combine);
Example:
var a = [{"fit":["34","32","30","28"],"size":["x"]}, {"fit": ["10", "11"], "size":["xxxxxxxxl"]}]
var b = [{"size":["s","m","xl"],"fit":["36"]}];
let combine = ({fit, size}, {fit: fit2, size: size2}) =>
({ fit: [...fit, ...fit2], size: [...size, ...size2] });
let result = [...a, ...b].reduce(combine);
console.log(result);
If you don't want to use the keys directly you could try
const arr3 = b.reduce((carry, current, index) => {
Object.keys(current)
.forEach(key => {
Object.assign(carry, { [key]: Array.prototype.concat.call(current[key], a[index][key])});
});
return carry;
}, {});

Get array of objects from array (of strings e.g.)

I need to get array of objects from array of strings.
For examaple:
var arr = ["1005", "1005", "1005", "1006", "1006", "1006", "1007", "1007"];
var result = arr.reduce((iss, index) => {
iss[index] = (iss[index] || 0) + 1;
return iss
}, {});
and the result would be
{1005: 3, 1006: 3, 1007: 2}
So is there a way to get next output:
[{"1005":3},{"1006":3},{"1007":2}]
If you really want that:
result = Object.entries(result).map(([key, value]) => ({[key]: value}));
You can extend result by iterating its keys
result = Object.keys ( result ).map( s => ({ [s] : result[s] }) );

Loop over object es6

I have an object which looks like this:
const object = {
head: 1,
eyes: 2,
arms: 2,
legs: 3
}
I want to loop over this object and this and log out each key name e.g. eyes for the amount of the value.
this would result in:
head
eyes
eyes
arms
arms
legs
legs
legs
Currently I have this solution but it feels like it could be done more neatly and readible.
Object.keys(object)
.map(key => {
return [...Array(object[key])].map( (_, i) => {
return console.log(key)
})
Any suggestions?
You could use Object.entries() and map() method and return new array.
const object = {head: 1,eyes: 2,arms: 2,legs: 3}
const res = [].concat(...Object.entries(object).map(([k, v]) => Array(v).fill(k)))
console.log(res)
Or you could use reduce() with spread syntax in array.
const object = {head: 1,eyes: 2,arms: 2,legs: 3}
const res = Object
.entries(object)
.reduce((r, [k, v]) => [...r, ...Array(v).fill(k)], [])
// Or you can use push instead
// .reduce((r, [k, v]) => (r.push(...Array(v).fill(k)), r), [])
console.log(res)
Object.entries(object)
.forEach(([key, times]) => console.log((key + "\n").repeat(times)));
One may use String.prototype.repeat...
"...it feels like it could be done more neatly and readible."
Recursion makes it pretty clean and understandable.
const object = {
head: 1,
eyes: 2,
arms: 2,
legs: 3
};
Object.entries(object).forEach(function f([k,v]) {
if (v) {
console.log(k);
f([k, --v]);
}
})
You can rearrange things a bit if you know the value will always be greater than 0.
const object = {
head: 1,
eyes: 2,
arms: 2,
legs: 3
};
Object.entries(object).forEach(function f([k,v]) {
console.log(k);
if (--v) f([k, v]);
})

Categories

Resources