5

I have the following object:

data = [
  { name: 'foo', type: 'fizz', val: 9 },
  { name: 'foo', type: 'buzz', val: 3 },
  { name: 'bar', type: 'fizz', val: 4 },
  { name: 'bar', type: 'buzz', val: 7 },
];

And used lodash map:

result = _.map(data, function item, idx){
  return {
    key: item[key],
    values: item.value,
  }
}

Which results in:

[
  { key: foo, val: 9 },
  { key: foo, val: 3 },
  { key: bar, val: 4 },
  { key: bar, val: 7 },
]

but now I'm trying to return:

[
  { key: 'foo', val: 12 },
  { key: 'bar', val: 11 },
]

I tried using reduce which seems to only output to a single object, which I could then convert back into an array, but I feel like there must be an elegant way to use lodash to go from my source data straight to my desired result without all of the intermediate steps.

I thought this was addressing my exact problem, but it seems like quite a bit of work just to have to convert the object into the desired array of objects outlined above.

Cheers.

4 Answers 4

9

A twist to the accepted answer that uses groupBy instead of reduce to do the initial grouping:

var result = _.chain(data)
    .groupBy('name')
    .map((group, key) => ({ key, val : _.sumBy(group, 'val') }))
    .value();
Sign up to request clarification or add additional context in comments.

1 Comment

If es6 is allowed it's a whole different ball game :) For example memo[obj.name = obj.val] += obj.val;, removing the undefined check, and return {key, val}
5

Interestingly not straight forward, because of wanting to accumulate the value by key, but then wanting the key as a value of the property key. So somewhat like an inverse map reduce:

var result = 
    _.chain(data)
        .reduce(function(memo, obj) {
            if(typeof memo[obj.name] === 'undefined') {
                memo[obj.name] = 0;
            } 
            memo[obj.name] += obj.val;
            return memo;
        }, {})
        .map(function (val, key) {
            return {key: key, val: val};
        })
        .value();

For the sake of brevity in es6:

_.chain(data)
    .reduce((memo, obj) => {
        memo[obj.name = obj.val] += obj.val;
        return memo; 
    }, {})
    .map((val, key) => ({key, val}))   
    .value();

1 Comment

Brilliant. Thank you. The wonky end result is to feed into d3 which seems to expect quirky data structures at times.
3

You can get all the unique names using map() and uniq(), and then map() each name to get their respective sums using sumBy().

var result = _(data)
  .map('name')
  .uniq()
  .map(key => ({ 
    key, 
    val: _(data).filter({ name: key }).sumBy('val')
  }))
  .value();

var data = [
  { name: 'foo', type: 'fizz', val: 9 },
  { name: 'foo', type: 'buzz', val: 3 },
  { name: 'bar', type: 'fizz', val: 4 },
  { name: 'bar', type: 'buzz', val: 7 }
];

var result = _(data)
  .map('name')
  .uniq()
  .map(key => ({ 
    key, 
    val: _(data).filter({ name: key }).sumBy('val')
  }))
  .value();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>

Here is a non es6 version:

var result = _(data)
  .map('name')
  .uniq()
  .map(function(key) { 
    return {
      key: key, 
      val: _(data).filter({ name: key }).sumBy('val')
    };
  })
  .value();

3 Comments

Wow, very elegant!
Actually, I just realized your totals are incorrect. They're including all of the items.
You're totally right, I updated the answer. I guess grouping it would have been the optimal solution and then use sumBy().
0

Here's a simple non-lodash version.

const data = [{
    name: 'foo',
    type: 'fizz',
    val: 9
  },
  {
    name: 'foo',
    type: 'buzz',
    val: 3
  },
  {
    name: 'bar',
    type: 'fizz',
    val: 4
  },
  {
    name: 'bar',
    type: 'buzz',
    val: 7
  },
]

const result = data.reduce((acc, curr) => {
  const index = acc.findIndex(item => item.name === curr.name)
  index > -1 ? acc[index].val += curr.val : acc.push({
    name: curr.name,
    val: curr.val
  })
  return acc
}, [])

console.log(result)

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.