0

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" 
  }
]

4 Answers 4

1

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);

Sign up to request clarification or add additional context in comments.

1 Comment

this solution works for me, it removes the for loop and aligns with the rest of the code.
1

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>

Comments

1

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);

3 Comments

If you need to have clean structure in your code if you are using lodash it would be better to replace reduce with _.reduce of lodash and Object.values with _.value also.
I agree lodash provide some really clean and elegant functions. But if one is clever, one can right clean code even without using lodash.
I agree with you and I had a conversation with my project manager about the point of using lodash every where in our project or just using it if we think that code would be more simple and readable but he said we must use lodash and this is a standard for us. and I think its depends on problem not the structure of code or other teammates
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.