Search code examples
javascriptecmascript-6reduce

Array of objects reducing based on multiple parameters


Need to reduce an array of Objects to return the highest value of the same name.

Need the following,

[
    {
        name: 'a',
        value: 20,
        ...(other object values)
    },
    {
        name: 'a',
        value: 80
        ...(other object values)
    },
    {
        name: 'b',
        value: 90,
        ...(other object values)
    },
    {
        name: 'b',
        value: 50,
        ...(other object values)
    }
]

To return

[
    {
        name: 'a',
        value: 80
        ...(other object values)
    },
    {
        name: 'b',
        value: 90,
        ...(other object values)
    }
]

I have a solution, but it seems a little to much, wondering if there is a more straightforward way to achieve it?

Below is the solution I could think of:

var toReduce = [
    {
        'name': 'a',
        'value': 20,
        'other': 'any',
    },
    {
        'name': 'a',
        'value': 80,
        'other': 'value',
    },
    {
        'name': 'b',
        'value': 90,
        'other': 'extra',
    },
    {
        'name': 'b',
        'value': 50,
        'other': 'super',
    }
];


function arrayReducer(arrayToReduce) {
    // Created an object separating by name
    let reduced = arrayToReduce.reduce((accumulator, currentValue) => {
        (accumulator[currentValue.name] =
            accumulator[currentValue.name] || []).push(currentValue);
        return accumulator;
    }, {});
    // Reduce object to the highest value
    for (let quotes of Object.keys(reduced)) {
        reduced[quotes] = reduced[quotes].reduce(
            (accumulator, currentValue) => {
                return accumulator && accumulator.value > currentValue.value
                    ? accumulator
                    : currentValue;
            }
        );
    }
    // return only object values
    return Object.values(reduced);
}

console.log(arrayReducer(toReduce));


Solution

  • The easiest here is probably a simple loop:

    const result = {};
    
    for (const item of input) {
      if (item.value > (result[item.name]?.value ?? 0)) {
        result[item.name] = item;
      }
    }
    
    // To turn it back into the original array format:
    const output = Object.values(result)
    

    Of course you can turn this into a single statement with reduce() and so on, but legibility will suffer as you can see from all the other answers.

    But here's the .reduce() version using the same approach:

    const output = Object.values(
      input.reduce( (acc, cur) => {
        if (cur.value > (acc[cur.name]?.value ?? 0)) {
          acc[cur.name] = cur;
        } 
        return acc;
      }, {})
    )