Reputation: 15239
I have an array of objects having a boolean field X. [{..., x: true, y: 3, ...]
I need to aggregate this array, in order to obtain a value, true
(or false
), if all values of x
are correspondingly true
(or false
), otherwise undefined
... and a sum of y
's...
is that possible to use the reduce
Array function, groupby by underscorejs, or another one for this purpose?
ex:
[
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]
result
{x: undefined, y: 9}
Upvotes: 4
Views: 16414
Reputation: 15239
thanks @Adassko, my variant was a little bit longer:
[
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]
.reduce((a, b, i) => ({
x : a.x === b.x || i == 0 ? b.x : undefined,
y : a.y + b.y
}))
Upvotes: 0
Reputation: 1074485
Although you can shoehorn this into a reduce
call (because any array operation can be shoehorned into a reduce
), there's no benefit to doing so. Just use a loop:
const result = {x: null, y: 0};
for (const entry of array) {
if (result.x === null) {
result.x = entry.x;
} else if (result.x !== entry.x) {
result.x = undefined;
}
result.y += entry.y;
}
Live Example:
function check(array) {
const result = {x: null, y: 0};
for (const entry of array) {
if (result.x === null) {
result.x = entry.x;
} else if (result.x !== entry.x) {
result.x = undefined;
}
result.y += entry.y;
}
console.log(result);
}
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: true, y: 6}
]);
console.log("---");
check([
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
But again, you can shoehorn that into a reduce
if you want by always returning the same object:
const result = array.reduce((obj, entry) => {
if (obj.x === null) {
obj.x = entry.x;
} else if (obj.x !== entry.x) {
obj.x = undefined;
}
obj.y += entry.y;
return obj;
}, {x: null, y: 0});
Live Example:
function check(array) {
const result = array.reduce((obj, entry) => {
if (obj.x === null) {
obj.x = entry.x;
} else if (obj.x !== entry.x) {
obj.x = undefined;
}
obj.y += entry.y;
return obj;
}, {x: null, y: 0});
console.log(result);
}
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: true, y: 6}
]);
console.log("---");
check([
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
But, if you want a reduce
solution and you don't mind creating a bunch of temporary throw-away objects, check out Adassko's answer. Simple and straight-forward, and 99.9% of the time, you don't care about the temporary object creation.
Upvotes: 3
Reputation: 5343
this is pretty straight-forward with reduce:
.reduce((a, b) => ({
x: a.x == b.x ? a.x : undefined,
y: a.y + b.y
}))
Live example:
var input = [
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
];
console.log(input.reduce((a, b) => ({
x: a.x == b.x ? a.x : undefined,
y: a.y + b.y
})));
Upvotes: 8
Reputation: 324
I came up with this solution using reduce
. Seems kind of hacky, but it should do the job. While reducing the array it determines if every x-value is equal, afterwards it sets the x-value of the reduced object accordingly.
let reduced = arr.reduce((acc, curr) => {
acc.x &= acc.x_init === curr.x;
acc.y += curr.y;
}, {x_init: arr[0].x, x: true, y: 0});
reduced.x = reduced.x ? reduced.x_init : undefined;
delete reduced.x_init;
Upvotes: 1