Search code examples
lodash

custom sum elements by key using lodash


I do have two objects containing keys like

var a = {bar:[1,2], foo:[7,9]}
var b = {bar:[2,2], foo:[3,1]}

I want to get the fallowing results:

var c = {bar:[3,4], foo:[10,10]}

I already have a for logic like:

for (let key in b) {
  if (a[key]) {
      a[key][0] += b[key][0];
      a[key][1] += b[key][1];
  } 
  else a[key] = b[key];
}

But I would like to make this logic in a lodash way. How can I Do it?


Solution

  • You can use create a function that takes n objects, and collects them to an array using rest parameters. Now you can spread the array into _.mergeWith() to combine the objects, and in the customizer function sum the items in the arrays using Array.map() or lodash's _.map() and _.add():

    const { mergeWith, isArray, map, add } = _
    
    const fn = (...rest) => _.mergeWith({}, ...rest, (o = [], s) =>
      map(s, (n, i) => add(n, o[i]))
    )
    
    const a = {bar:[1,2], foo:[7,9]}
    const b = {bar:[2,2], foo:[3,1]}
    const c = {bar:[3,2], foo:[5,6]}
    const d = {bar:[4,2], foo:[5,4]}
    
    const result = fn(a, b, c, d)
    
    console.log(result)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

    You can also use lodash/fp to create a function that merges all values to a multidimensional array with _.mergeAllWith(), then transpose the arrays using _.zipAll(), and sums each array:

    const { rest, flow, mergeAllWith, isArray, head, mapValues, zipAll, map, sum } = _
    
    const fn = rest(flow(
      mergeAllWith((o, s) => [...isArray(head(o)) ? o : [o], s]), // combine to a multidimensional array
      mapValues(flow(
        zipAll,
        map(sum)
      )),
    ))
    
    const a = {bar:[1,2], foo:[7,9]}
    const b = {bar:[2,2], foo:[3,1]}
    const c = {bar:[3,2], foo:[5,6]}
    const d = {bar:[4,2], foo:[5,4]}
    
    const result = fn(a, b, c, d)
    
    console.log(result)
    <script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>