serge
serge

Reputation: 15239

Aggregate an array of objects in JavaScript

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

Answers (4)

serge
serge

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

T.J. Crowder
T.J. Crowder

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

Adassko
Adassko

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

Josef
Josef

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

Related Questions