2

Let's say I have an object

const testArray = [{key: 'a', value: 5}, {key: 'b', value: 1}, {key: 'a', value: 2}]

what I want is

newArray = [{key: 'a', value: 3}, {key: 'b', value: 1}]

What I tried is

testArray.reduce((acc, cur, index) => {
    const exists = !!acc && acc.find(item => item.key === cur.key)
    if (!exists){
       acc.push(cur)
    } else {
       // can't figure out what i should do here
    }
    return acc;
}, [])

Or Else any other easy solution is appreciated.

Thanks

  • Welcome to stackoverflow community. I think if key node is key (unique identifier) whose value is a, b ... then you should not generate array like `[{key: 'a', value: 5}, {key: 'b', value: 1}, {key: 'a', value: 2}]`. If you have merged array `[{key: 'a', value: 5}, {key: 'b', value: 1}]` and `[{key: 'a', value: 2}]` then unique identifier must be unique. Please explain question more in details, if possible provide simplest example of problem. – Suson Waiba Apr 17 '21 at 06:24
  • I receive this format directly from database i.e. its not the result of merging arrays and i need to normalize this in frontEnd. – Tilak Basyal Apr 17 '21 at 06:29

3 Answers3

1

Note that when you use Array#find, the result value will keep the reference. So when you change the content it will affect the array acc.

const testArray = [{key: 'a', value: 5}, {key: 'b', value: 1}, {key: 'a', value: 2}];

const result = testArray.reduce((acc, cur) => {
  const exists = acc.find(item => item.key === cur.key)
  if (!exists) acc.push(cur);
  else exists.value -= cur.value;
  return acc;
}, []);

console.log(result);
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
1

You can take advantage of Array.prototype.reduce combine with Array.prototype.findIndex to update the result array like this

const testArray = [{key: 'a', value: 5}, {key: 'b', value: 1}, {key: 'a', value: 2}]

let result = testArray.reduce((accumulator, current, index)=>{
  let itemExists = accumulator.findIndex(item => {
    return item.key == current.key;
  });
  
  if(itemExists !== -1){
      current = {...current, value: accumulator[itemExists].value - current.value};
      accumulator[itemExists] = current;
      return accumulator;
  } else {
     return [...accumulator,current];
  }
},[])

console.log(result);

The idea is when the current item doesn't exists in the result array we just add it to the array otherwhise we update the value of the existing one by updating It value key with the existing one value key minus the value key of the current item value

Yves Kipondo
  • 5,289
  • 1
  • 18
  • 31
0

You can use Map where the keys are the key property from the objects and values are the entire object.

Now for every object in the testArray check if its key is present in the Map, if it is present, then update only the value, and it is not present set the entire value.

Solution Using Map

const testArray = [{ key: "a", value: 5 }, { key: "b", value: 1 }, { key: "a", value: 2 }];

const res = Array.from(testArray.reduce(
  (m, o) => (
    m.has(o.key)
      ? m.set(o.key, { ...m.get(o.key), value: m.get(o.key).value - o.value })
      : m.set(o.key, { ...o }),
    m
  ),
  new Map()
).values());

console.log(res)

Same solution but in a more readable format

const testArray = [{ key: "a", value: 5 }, { key: "b", value: 1 }, { key: "a", value: 2 }];

const res = Array.from(testArray.reduce(
  (m, o) => {
    if (m.has(o.key)) {
      const currVal = m.get(o.key);
      m.set(o.key, {...currVal, value: currVal.value - o.value})
    } else {
      m.set(o.key, {...o})
    }
    return m;
  },
  new Map()
).values());

console.log(res)

One Liner Using Objects

If the key is not present in the object then create an object where the value property is double the actual value.

Now for every object, just subtract the current value with the already existing value.

const 
   testArray = [{ key: "a", value: 5 }, { key: "b", value: 1 }, { key: "a", value: 2 }],
   
   res = testArray.reduce((m, {key, value}) => (m[key] ??= ((value) => ({key, value}))(2*value), m[key].value -= value, m), {});


console.log(Object.values(res))
Som Shekhar Mukherjee
  • 4,701
  • 1
  • 12
  • 28