Leonardo Uribe
Leonardo Uribe

Reputation: 123

How to get a single array with no-repeated items, with the sum of their value(s). ordered desc

If I have the following Array:

var myArray = [
 {sa67g:{id:'sa67g', name: 'Leo', value: 50}},
 {sa86w:{id:'sa86w', name: 'Amy', value: 40}},
 {sa33p:{id:'sa33p', name: 'Alex', value: 30}},
 {sa74x:{id:'sa74x', name: 'John', value: 20}},
 {sa67g:{id:'sa67g', name: 'Leo', value: 10}},
 {sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]

What is the best one to get a single array with no-repeated items, with the sum of their value(s) and ordered by descending value for all the repeated items from another array using lodash?

Te expected result should be something like this:

result = [{sa67g:{id:'sa67g', name: 'Leo', value: 60}},
 {sa33p:{id:'sa33p', name: 'Alex', value: 45}},
 {sa86w:{id:'sa86w', name: 'Amy', value: 40}},
 {sa74x:{id:'sa74x', name: 'John', value: 20}}
]

Upvotes: 0

Views: 82

Answers (6)

Ori Drori
Ori Drori

Reputation: 191976

You can use lodash's _.mergeWith() to combine all objects into a single object. Since _.mergeWith is recursive, the inner properties will be merged as well, and we can use this to sum the value property. Afterwards, we convert the object back to an array using _.map():

const myArray = [{"sa67g":{"id":"sa67g","name":"Leo","value":50}},{"sa86w":{"id":"sa86w","name":"Amy","value":40}},{"sa33p":{"id":"sa33p","name":"Alex","value":30}},{"sa74x":{"id":"sa74x","name":"John","value":20}},{"sa67g":{"id":"sa67g","name":"Leo","value":10}},{"sa33p":{"id":"sa33p","name":"Alex","value":15}}];

const result = _.map(
  // merge all objects into a single object, and sum the value property
  _.mergeWith({}, ...myArray, (objValue = 0, srcValue = 0, key) => key === 'value' ? objValue + srcValue : undefined),
  // split back into an array of objects
  (v, k) => ({ [k]: v })
)

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

Upvotes: 1

Mulan
Mulan

Reputation: 135227

Object properties should never be a value (ie, a key or id) – to create a key-value association, use a Map instead.

const concat = ({ id: id1, name: name1, value: v1 }, { id: id2, name: name2, value: v2 }) =>
  ({ id: id1, name: name1, value: v1 + v2 })

const main = xs =>
  xs.map (x => Object.entries (x) [ 0 ])
    .reduce ((acc, [ key, value ]) =>
      acc.has (key)
        ? acc.set (key, concat (value, acc.get (key)))
        : acc.set (key, value), new Map)

const myArray =
  [ {sa67g:{id:'sa67g', name: 'Leo', value: 50}}
  , {sa86w:{id:'sa86w', name: 'Amy', value: 40}}
  , {sa33p:{id:'sa33p', name: 'Alex', value: 30}}
  , {sa74x:{id:'sa74x', name: 'John', value: 20}}
  , {sa67g:{id:'sa67g', name: 'Leo', value: 10}}
  , {sa33p:{id:'sa33p', name: 'Alex', value: 15}}
  ]

console.log (main (myArray))
// => Map {
//   'sa67g' => { id: 'sa67g', name: 'Leo', value: 60 },
//   'sa86w' => { id: 'sa86w', name: 'Amy', value: 40 },
//   'sa33p' => { id: 'sa33p', name: 'Alex', value: 45 },
//   'sa74x' => { id: 'sa74x', name: 'John', value: 20 } }

If you want to convert the Map back to an object, you can use this

const mapToObject = m =>
  Array.from(m.entries ()).reduce ((acc, [ k, v ]) =>
    Object.assign (acc, { [ k ]: v }), {})

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386600

You could take a step by step approach and take the resutl for the next function with a pipe.

var array = [{ sa67g: { id: 'sa67g', name: 'Leo', value: 50 } }, { sa86w: { id: 'sa86w', name: 'Amy', value: 40 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 30 } }, { sa74x: { id: 'sa74x', name: 'John', value: 20 } }, { sa67g: { id: 'sa67g', name: 'Leo', value: 10 } }, { sa33p: { id: 'sa33p', name: 'Alex', value: 15 } }],
    pipe =  (...fn) => arg => fn.reduce((x, f) => f(x), arg),
    objects = array => array.map(o => Object.assign({}, Object.values(o)[0])),
    sum = array => array.reduce((m, o) => {
        if (m.has(o.id)) {
            m.get(o.id).value += o.value;
        } else {
            m.set(o.id, o);
        }
        return m;
    }, new Map),
    values = map => Array.from(map.values()),
    sort = array => array.sort((a, b) => b.value - a.value),
    map = array => array.map(o => ({ [o.id]: o })),
    path = pipe(objects, sum, values, sort, map),
    result = path(array);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Zac Delventhal
Zac Delventhal

Reputation: 4010

I'm sure there are a bunch of ways to do it. Off the top of my head, I would use reduce to convert your array of objects into one object with summed values. Using chain to combine with some other lodash methods for ordering and transforming, it would look like this:

const result = _.chain(myArray)
  .map(person => _.values(person))
  .flatten()
  .reduce((summed, person) => {
    if (!summed[person.id]) {
      summed[person.id] = person
    } else {
      summed[person.id].value += person.value
    }
    return summed
  }, {})
  .values()
  .orderBy(['value'], ['desc'])
  .map(person => ({ [person.id]: person }))
  .value()

Upvotes: 1

charlietfl
charlietfl

Reputation: 171669

Using native methods and assuming es8 support:

var myArray = [
 {sa67g:{id:'sa67g', name: 'Leo', value: 50}},
 {sa86w:{id:'sa86w', name: 'Amy', value: 40}},
 {sa33p:{id:'sa33p', name: 'Alex', value: 30}},
 {sa74x:{id:'sa74x', name: 'John', value: 20}},
 {sa67g:{id:'sa67g', name: 'Leo', value: 10}},
 {sa33p:{id:'sa33p', name: 'Alex', value: 15}},
]

var tmp = myArray.map(o => Object.values(o)[0])
  .reduce((a, c) => {
    const obj = a[c.name]
    if (obj) {
      obj.id = c.id > obj.id ? c.id : obj.id;// set highest id
      obj.value += c.value; //increment vlues
    } else {
      a[c.name] = Object.assign({},c);
    }
    return a;
  }, {});

var res = Object.values(tmp)
  .sort((a, b) => b.value - a.value)
  .map(o => ({[o.id]:o }))
console.log(res)
.as-console-wrapper {	max-height: 100%!important;}

Upvotes: 0

Sami Hult
Sami Hult

Reputation: 3082

  1. Easiest way to filter out single values would be to use a Set. Use it to get a set of unique keys.

  2. Create the desired object by iterating the keys and calculating the sums.

Upvotes: 0

Related Questions