Home > Back-end >  filter and sum array of objects in javascript
filter and sum array of objects in javascript

Time:05-26

Hello and thank you for checking my question.

I have an array of objects which contains multiple entries for the same person corresponding to different dates. I need to sum the values for each person.

const data = [
  {
    name: "Bob",
    date: 3/27/22
    value: 300
  },
  {
    name: "Alice",
    date: 1/13/22
    value: 500
  },
  {
    name: "Bob",
    date: 5/13/22
    value: 400
  },
  {
    name: "Alice",
    date: 4/19/22
    value: 350
  },
  {
    name: "John",
    date: 2/15/22
    value: 700
  },
]

I need the result to be:

const result = [
  {
    name: "Bob",
    value: 700
  },
  {
    name: "Alice",
    value: 850
  },
  {
    name: "John",
    value: 700
  },
]

How can I do this in the most efficient way possible?

So far, I have only been able to achieve this by using the filter array method returning the name value, pushing the result to a new array, and summing that array. However, I do not know all of the name values in advance so this wont work.

Thank you for your time

CodePudding user response:

I think this approach has reasonable performance :

const data = [
  {
    name: "Bob",
    value: 300
  },
  {
    name: "Alice",
    value: 500
  },
  {
    name: "Bob",
    value: 400
  },
  {
    name: "Alice",
    value: 350
  },
  {
    name: "John",
    value: 700
  },
];

const reduceData = (data) => data.reduce((acc, cur) => {
  const {name, value} = cur;                            // Get name and value from current item
  const item = acc.find(it => it.name === name);        // Find in our accumulator the desired object
  item ? item.value  = value : acc.push({name, value}); // Update object or create a new object if it doesn't exist
  return acc;                                           // Return accumulator
} , []);

console.log(reduceData(data));

Array.reduce takes an internal function as first parameter.

This internal function usually takes two parameters: one will be the "accumulator" that will be returned by the function and the other will be the "current array item".

Array.reduce will run the internal function on every array item and finally return its value.

The "accumulator" parameter passed to the internal function on every run is the return value of the previous internal function call.

Array.reduce can have an additional parameter which is the initial accumulator value passed to the first internal function call (here we use an empty array).

More info here -> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

CodePudding user response:

One way to do this can be this:

const data = [
  {
    name: 'Bob',
    date: '3/27/22',
    value: 300,
  },
  {
    name: 'Alice',
    date: '1/13/22',
    value: 500,
  },
  {
    name: 'Bob',
    date: '5/13/22',
    value: 400,
  },
  {
    name: 'Alice',
    date: '4/19/22',
    value: 350,
  },
  {
    name: 'John',
    date: '2/15/22',
    value: 700,
  },
];

let res = data.reduce((agg, curr) => {
  agg[curr.name] = (agg[curr.name] || 0)   curr.value;
  return agg;
}, {});

const res2 = Object.keys(res).map((v) => {
  return {
    name: v,
    value: res[v],
  };
});

console.log(res2);

CodePudding user response:

You can do this by mapping also

const data = [
    {
        name: "Bob",
        date: ' 3 / 27 / 22',
        value: 1,
    },
    {
        name: "Alice",
        date: '1 / 13 / 22',
        value: 2,
    },
    {
        name: "Bob",
        date: '5 / 13 / 22',
        value: 1,
    },
    {
        name: "Alice",
        date: ' 4 / 19 / 22',
        value: 2
    },
    {
        name: "John",
        date: '2 / 15 / 22',
        value: 3
    },
]
const res = {};
for (let i = 0; i < data.length; i  ) {

    if (res[data[i].name]) { //here if any key(name) is already present in res then we add the value in already present value of that name
        res[data[i].name].value  = data[i].value
    } else {
        res[data[i].name] = data[i] //here we are inserting data into res object if doesn't find any key with already name present in it
    }
}



const res2 = Object.keys(res).map(person => {
    return {
        name: person,
        value: res[person].value
    }
})

console.log(res2);

CodePudding user response:

You can merge the value with the same property of key name by using reduce function.

const mergedData = data.reduce((prev, curr) => {
      if(prev[curr.name]) {
         // Sum the value if the name matches for multiple objects
         prev[curr.name].value = prev[curr.name].value   curr.value
      } else {
         // Return the actual objects if not matches any
         prev[curr.name] = curr
      }
         return prev
}, {});

console.log(Object.values(mergedData))

The output would be:

[
  {
    "name": "Bob",
    "date": "3/27/22",
    "value": 700
  },
  {
    "name": "Alice",
    "date": "1/13/22",
    "value": 850
  },
  {
    "name": "John",
    "date": "2/15/22",
    "value": 700
  }
]

To know more about reduce you can learn from the documentation here

CodePudding user response:

This is an (probably flawed) attempt to compare the answers on this page for their speed. Whether it is helpful or not could depend greatly on what the actual datasets that are being processed look like. The data array here is a wild (probably wrong) guess at what a worst case scenario could be like.

Feel free to fiddle with it.

const rand_letter = () => String.fromCharCode(65 Math.floor(Math.random() * 26));
const data = Array.from({ length: 10000 }, () => ({ name: rand_letter()   rand_letter(), value: Math.floor(Math.random() * 1000) }));


const A = (data) => {
  let name_map = new Map();

  for(let i = 0; i < data.length; i  ) {
    const { name, value } = data[i];

    name_map.set(name, (name_map.get(name) ?? 0)   value);
  }

  return [...name_map.entries()].map(([name, value]) => ({ name, value }));
};

const B = (data) => {
  let name_map = {};

  for(let i = 0; i < data.length; i  ) {
    const { name, value } = data[i];

    name_map[name] = (name_map[name] ?? 0)   value;
  }

  return Object.entries(name_map).map(([name, value]) => ({ name, value }));
};

const C = (data) =>
  Object.entries(
    data.reduce((acc, { name, value }) => {
      acc[name] = (acc[name] ?? 0)   value;
      return acc;
    }, {})
  ).map(([name, value]) => ({ name, value }));

const D = (data) =>
  data.reduce((acc, cur) => {
    const {name, value} = cur;
    const item = acc.find(it => it.name === name);
    item ? item.value  = value : acc.push({name, value});
    return acc;
  }, []);


const time = (fn) => {
  const iter = 100;
  const t0 = performance.now();
  
  for(let i = 0; i < iter; i  )
    fn(data);
  
  console.log('time for '   fn.name   ' (milliseconds)', (performance.now() - t0) / iter);
};

[A, B, C, D].forEach(time);

  • Related