user990951
user990951

Reputation: 1479

lodash sum of array of objects

I just started learning about lodash, and trying to incorporate it in my code. As you can see I am adding the weights of each of the elements under employee. The issue is I am having is I am not sure how to get the third element "gender" into my final array of data. I used this Reduce array of objects on key and sum value into array to construct my lodash code. You can see below on how am trying to get my output.

var userAns = [{
    id: 1,
    text: "answer1",
    employee: [{
        name: "John",
        weight: 3,
        gender: "male"
      },
      {
        name: "Sally",
        weight: 4,
        gender: "female"
      }
    ]
  },
  {
    id: 2,
    text: "answer2",
    employee: [{
        name: "John",
        weight: 6,
        gender: "male"
      },
      {
        name: "Sally",
        weight: 3,
        gender: "female"
      }
    ]
  }
];

var scores = [];
for (var i = 0; i < userAns.length; i++) {
  for (var j = 0; j < userAns[i].employee.length; j++) {
    scores.push(userAns[i].employee[j]);
  }
}

var result = _(scores)
  .map('name')
  .uniq()
  .map(function(key) {

    return {
      name: key,
      score: _(scores).filter({
        name: key
      }).sumBy('weight')
    };
  })
  .value();

console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.14.1/lodash.min.js"></script>

Here is how I am trying to get my array

[
  {
    "name": "John",
    "score": 9,
    "gender": "male"
  },
  {
    "name": "Sally",
    "score": 7,
    "gender":"female" 
  }
]

Upvotes: 0

Views: 9695

Answers (4)

Ori Drori
Ori Drori

Reputation: 191936

Using lodash:

You can use a combination of _.flatMap(), _.groupBy(), _.map() and _.mergeWith():

var userAns = [{"id":1,"text":"answer1","employee":[{"name":"John","weight":3,"gender":"male"},{"name":"Sally","weight":4,"gender":"female"}]},{"id":2,"text":"answer2","employee":[{"name":"John","weight":6,"gender":"male"},{"name":"Sally","weight":3,"gender":"female"}]}];

var result = _(userAns)
  .flatMap('employee') // create an array of all emplyoees
  .groupBy('name') // create groups of employees with the same name
  .map(function(g) {
    // merge each group to a single object, and combine weight values
    return _.mergeWith.apply(_, [{}].concat(g, function(obj, src, key) {
      return key === 'weight' ? (obj || 0) + src : undefined;
    }));
  })
  .value();
  
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Using ES6:

Use Array#map to get an array of employee arrays, and flatten by spreading into Array#concat. Reduce the array into a Map, then convert back to an array by spreading the Map#values iterator:

const userAns = [{"id":1,"text":"answer1","employee":[{"name":"John","weight":3,"gender":"male"},{"name":"Sally","weight":4,"gender":"female"}]},{"id":2,"text":"answer2","employee":[{"name":"John","weight":6,"gender":"male"},{"name":"Sally","weight":3,"gender":"female"}]}];

const result = [...
  [].concat(...userAns.map(({ employee }) => employee)) // combine all employees to a single array
  .reduce((m, o) => { // reduce similar employees to a single object in a Map, and combine weight
    // if the name object doesn't exists create a new one by cloning the object, and assigning weight: 0
    m.has(o.name) || m.set(o.name, Object.assign({}, o, { weight: 0 }));
    
    m.get(o.name).weight += o.weight; // add the current weight to the name object
    
    return m;
  }, new Map())
  .values()]; // get the Map values, and spread to convert to array
  
console.log(result);

Upvotes: 1

Vipin Kumar
Vipin Kumar

Reputation: 6546

You can do this by doing minor modification. First get your user inside map like below

var user = _(scores).filter({
  name: key
});

Then, you can extract score's sum and gender from it as following

return {
  name: key,
  score: user.sumBy('weight'),
  gender: user.get('[0]').gender
};

Please find modified snippet below

var userAns = [{
  id: 1,
  text: "answer1",
  employee: [{
    name: "John",
    weight: 3,
    gender: "male"
  }, {
    name: "Sally",
    weight: 4,
    gender: "female"
  }]
}, {
  id: 2,
  text: "answer2",
  employee: [{
    name: "John",
    weight: 6,
    gender: "male"
  }, {
    name: "Sally",
    weight: 3,
    gender: "female"
  }]
}];

var scores = [];
for (var i = 0; i < userAns.length; i++) {
  for (var j = 0; j < userAns[i].employee.length; j++) {
    scores.push(userAns[i].employee[j]);
  }
}


var result = _(scores)
  .map('name')
  .uniq()
  .map(function(key) {
    var user = _(scores).filter({
      name: key
    });
    return {
      name: key,
      score: user.sumBy('weight'),
      gender: user.get('[0]').gender
    };
  })
  .value();

console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.14.1/lodash.min.js"></script>

Upvotes: 0

Hassan Imam
Hassan Imam

Reputation: 22524

You can accomplish this without lodash. You can use array#reduce to first accumulate all your employee array inside each userAns and then again using array#reduce you can sum up the weight. Then, get the output using Object.values()

var userAns = [{ id: 1, text: "answer1", employee: [{ name: "John", weight: 3, gender: "male" }, { name: "Sally", weight: 4, gender: "female" } ] }, { id: 2, text: "answer2", employee: [{ name: "John", weight: 6, gender: "male" }, { name: "Sally", weight:3, gender: "female" }]}];

var result = userAns
              .reduce((r,o) => r.concat(o.employee),[])
              .reduce((r,o) => {
                  if(r[o.name])
                      r[o.name].weight += o.weight;
                  else
                    r[o.name] = o;
                  return r;
              },{});
              
var output = Object.values(result);
console.log(output);

Upvotes: 1

Rahul Singh
Rahul Singh

Reputation: 19622

You can make use of lodash find method and then pluck the key which you want from the Object and get the value of the Object using values and assign it to gender key of the final resultant Object

var userAns = [{
    id: 1,
    text: "answer1",
    employee: [{
        name: "John",
        weight: 3,
        gender: "male"
      },
      {
        name: "Sally",
        weight: 4,
        gender: "female"
      }
    ]
  },
  {
    id: 2,
    text: "answer2",
    employee: [{
        name: "John",
        weight: 6,
        gender: "male"
      },
      {
        name: "Sally",
        weight: 3,
        gender: "female"
      }
    ]
  }
];

var scores = [];
for (var i = 0; i < userAns.length; i++) {
  for (var j = 0; j < userAns[i].employee.length; j++) {
    scores.push(userAns[i].employee[j]);
  }
}

var result = _(scores)
  .map('name')
  .uniq()
  .map(function(key) {
    return {
      name: key,
      gender: _.values(_.pick(_.find(scores, (d) => d.name == key),'gender'))[0],
      score: _(scores).filter({
        name: key
      }).sumBy('weight')
    };
  })
  .value();

console.log(result);
<script src="https://cdn.jsdelivr.net/lodash/4.14.1/lodash.min.js"></script>

Upvotes: 1

Related Questions