How can i combine the reduce and forEach - javascript

How can I combine this reduce and forEach so that we only traverse the list one time.
obj.arr = (obj.arr || []).reduce((newArr, arr2) => {
if (arr2.name !== anyProperty) {
newArr.push(arr2);
}
return newArr;
}, []);
obj.arr.forEach((arr2) => {
obj.arr[arr2.name] = arr2;
});

Check this code:
let anyProperty = 'test';
let obj = {
arr: [
{ name: 'a', value: 1 },
{ name: 'test', value: 2 },
{ name: 'b', value: 3 },
]
};
obj.arr = Object.fromEntries((obj.arr || [])
.filter(item => item.name !== anyProperty)
.map(item => [item.name, item]));
console.log(obj.arr);

Related

Merge n amount of objects from array into one array based on id [duplicate]

This question already has answers here:
Merge n object from array into one array based on id
(4 answers)
Closed 3 years ago.
I'm trying to merge n objects from an array of objects listed below.
I tried to use reduce method, but I can't understand what I'm doing wrong, still new to advance js methods.
const array = [
{
data: {
'1': {
foo: 'bar',
test: true
},
'4': {
foo: 'boor'
}
}
},
{
data: {
'1': {
x: 'o',
test2: false
}
}
}
];
const result = Object.values(
array.reduce((r, { data }) => {
Object.entries(data).forEach(([id, { ...else }]) => {
r[id] = r[id] || {
id,
fooValue: else.foo, // should be `bar` for id `1` and `boor` for id `4`
xValue: else.x, // should be `o` for id `1` and `undefined` for id `4`
all: ...else
};
});
return r;
}, {})
);
I'm trying to get something like this in a end, but I'm pretty lost.
[
{
id: '1',
fooValue: 'bar',
xValue: 'o',
all: {
foo: 'bar',
test: true,
x: 'o',
test2: false
}
},
{
id: '4',
fooValue: 'boor',
xValue: undefined,
all: {
foo: 'boor'
}
}
]
const array = [
{
data: {
'1': {
foo: 'bar',
test: true
},
'4': {
foo: 'boor'
}
}
},
{
data: {
'1': {
x: 'o',
test2: false
}
}
}
];
let result = Object.values(array.reduce((acc, c) => {
let list = Object.entries(c.data);
list.map( o => {
let key = o[0];
acc[key] = (acc[key] || {});
acc[key]['id'] = key;
acc[key]['fooValue'] = o[1]['foo'];
acc[key]['xValue'] = o[1]['x'];
acc[key]['all'] = {...acc[key]['all'], ...o[1]};
});
return acc;
}, {}));
console.log(result);
//or
let result1 = Object.values(array.reduce((acc, c) => {
let list = Object.entries(c.data);
list.map( o => {
let key = o[0];
let value = o[1];
acc[key] = (acc[key] || {});
acc[key] = {
id: key,
fooValue: value['foo'],
xValue: value['x'],
all: {...acc[key]['all'], ...o[1]}
}
});
return acc;
}, {}));
console.log(result1);
else is a keyword in Javascript. You cannot use it as a variable. Besides, you should wrap the variable with spread operator with curly braces so as to copy the object
const result = Object.values(
array.reduce((r, { data }) => {
Object.entries(data).forEach(([id, rest ]) => {
r[id] = r[id] || {
id: id,
fooValue: rest.foo,
xValue: rest.x,
all: rest
};
});
return r;
}, {})
);

create unique list and count of array items in array of objects

I have the following source array:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
and i want to get the unique student names and their count, final result should be like:
{
'john': 3,
'sarah': 2,
'thomas': 1,
'jack': 1,
'peter': 1,
'bob': 1
}
here is my attempt:
const unique = list.reduce(function(total, curr){
const students = curr.students;
for (c of students) {
if (!total[c]) {
total[c] = 1
} else {
total[c] += 1;
}
}
return total;
}, {});
is there a better way to do it? or faster and cleaner way? thanks
I'd flatten the arrays first, then count up with reduce:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
console.log(count);
If you want the properties to be ordered as well, then take the Object.entries of the object, sort it, then turn it back into an object with Object.fromEntries:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = list.flatMap(({ students }) => students);
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
const sorted = Object.fromEntries(
Object.entries(count).sort((a, b) => b[1] - a[1])
);
console.log(sorted);
If your environment doesn't support flatMap, or fromEntries, use a polyfill, or flatten/group with a different method:
const list = [
{
students: [ 'peter', 'bob', 'john']
},
{
students: [ 'thomas', 'sarah', 'john']
},
{
students: [ 'john', 'sarah', 'jack']
}
];
const allStudents = [].concat(...list.map(({ students }) => students));
const count = allStudents.reduce((a, name) => {
a[name] = (a[name] || 0) + 1;
return a;
}, {});
const sortedEntries = Object.entries(count).sort((a, b) => b[1] - a[1]);
const sortedObj = sortedEntries.reduce((a, [prop, val]) => {
a[prop] = val;
return a;
}, {});
console.log(sortedObj);
Keep in mind that object property order is only specified in ES6+ environments. While Object.fromEntries isn't guaranteed by the specification to create an object in the same order as the entries, it does anyway, in any implementation I've ever encountered, luckily. (If you're still worried about it, you can use the old-fashioned reduce method to create the object instead, like in the third snippet)
Try using functional programming: combination of map and 2 reduce methods.
const listMapped = list.map(it=> it.students)
const listReduced = listMapped.reduce((acc, rec) => {
return [...acc.concat(rec)]
}, [])
const listCounted = listReduced.reduce((acc, rec) => {
acc[rec]
? acc[rec] += 1
: acc[rec] = 1
return acc
}, {})
console.log(listCounted)

Dynamic solution to group by an array of objects

I'm trying to do a group by over an array of objects. The array that I'm trying to group by is subject to change and I need a solution that's dynamic.
This is how the array that i'm trying to work on looks like.
const arr = [
{
first: {
label: 'a',
key: 'a'
},
second: {
label: 'b',
key: 'b',
}
},
{
first: {
label: 'aa',
key: 'aa'
},
second: {
label: 'bb',
key: 'bb',
}
}
]
I've tried this so far:
const result = arr.reduce((acc, curr) => {
acc['first'] = acc['first'] || [];
acc['second'] = acc['second'] || [];
acc['first'].push(curr.first);
acc['second'].push(curr.second);
return acc;
}, {});
This solves my problem, but it's not a dynamic solution.
This is the expected result:
const obj = {
first: [
{
label: 'a',
key: 'a'
},
{
label: 'aa',
key: 'aa'
}
],
second: [
{
label: 'b',
key: 'b'
},
{
label: 'bb',
key: 'bb'
}
]
}
To make this more general, you simply need to loop over the keys, rather than hardcoding your first/second code. That should look something like this:
const result = arr.reduce((acc, curr) => {
let keys = Object.keys(curr);
keys.forEach((key) => {
acc[key] = acc[key] || [];
acc[key].push(curr[key]);
});
return acc;
}, {});
You can use reduce and Object.entries
const arr = [{first: {label: 'a',key: 'a'},second: {label: 'b',key: 'b',}},{first: {label: 'aa',key: 'aa'},second: {label: 'bb',key: 'bb',}}]
let final = arr.reduce((op, inp) => {
Object.entries(inp).forEach(([key, value]) => {
op[key] = op[key] || []
op[key].push(value)
})
return op
},{})
console.log(final)

How to merge 3 javascript object with modified value

I have 3 objects like
[
const arr = [
{name:'ABC', value:123},
{name:'ABC', value:456},
{name:'ABC',value:789},
{name:'DEF',value:9999},
name:'DEF', value:0000}
]
i want output like
updatedArr = [
{name:'ABC', value:123, value1:456, value2:789}
{name:'DEF', value:9999, value1:0000}
]
any kind of links regarding this will be also helpful.
You could use reduce method to create an object and then Object.values to get an array of values.
const arr = [{name:'ABC', value:123},{name:'ABC', value:456},{name:'ABC',value:789},{name:'DEF',value:9999},{name:'DEF', value:0000}]
const res = arr.reduce((r, e) => {
if(!r[e.name]) r[e.name] = {...e}
else {
let {name, ...rest} = r[e.name];
r[e.name][`value${Object.keys(rest).length}`] = e.value
}
return r;
}, {});
console.log(Object.values(res))
const arr = [{
name: 'ABC',
value: 123
},
{
name: 'ABC',
value: 456
},
{
name: 'ABC',
value: 789
},
{
name: 'DEF',
value: 9999
},
{
name: 'DEF',
value: 0000
}
]
const res = Object.values(arr.reduce((acc, item) => {
if (!acc[item.name]) {
acc[item.name] = item;
} else {
acc[item.name]['value' + (Object.keys(acc[item.name]).length - 1)] = item.value;
}
return acc;
}, {}));
console.log(res)
use object assignation:
Object.assign(ob1,ob2);

How to manipulate an array by grouping items into arrays

Let's say I have this array:
let obj = [
{ prop: 'a', val: 2 },
{ prop: 'a', val: 1 },
{ prop: 'b', val: 3 },
{ prop: 'b', val: 1 }
]
Let's say i want to get an array for each existing prop val:
let myArr = [
[ { prop: 'a', val: 2 }, { prop: 'a', val: 1 } ]
[ { prop: 'b', val: 3 }, { prop: 'b', val: 1 } ]
]
how would you do that by using high order functions?
function sumProps(arr) {
const sum = arr.reduce((ac,cv) => {
if (ac[cv] === undefined) ac[cv.prop] = ac[cv.prop] = cv.val;
else ac[cv.prop] += cv.val;
}, {});
const res = [];
for (const p in sum) {
res.push({ prop:p, sum:sum[p] });
}
return res;
}
You could reduce the objects by looking for an object with same prop and update the value.
var array = [{ prop: 'a', val: 2 }, { prop: 'a', val: 1 }, { prop: 'b', val: 3 }, { prop: 'b', val: 1 }],
result = array.reduce((r, o) => {
var temp = r.find(({ prop }) => prop === o.prop);
if (temp) {
temp.val += o.val;
} else {
r.push(Object.assign({}, o));
}
return r
}, []);
console.log(result);

Categories