I have a large javascript array of some people Bought a car in different years. the simplified array is like this:

const owners = [{
  name: "john",
  hasCar: true,
  yearBought: 2002
}, {
  name: "john",
  hasCar: true,
  yearBought: 2005
}, {
  name: "mary",
  hasCar: true,
  yearBought: 2015
}, {
  name: "john",
  hasCar: true,
  yearBought: 2018
}]

if a person has more than one car (like John in this example), there is different objects for him with different years he has bought the car. I want to merge objects belongs to each individual person and the final result should be like this:

const owners = [{
  name: "john",
  hasCar: true,
  yearBought: [2002, 2005, 2018]
}, {
  name: "mary",
  hasCar: true,
  yearBought: 2018
}]

5 Answers

1
adiga On Best Solutions

You could use reduce the array and group them based on name first. The accumulator is an object with each unique name as key. If the name already exists, use concat to push them into the array. Else, create a new key in the accumulator and set it to the current object. Then, use Object.values() to get the values of the array as an array

const owners = [{name:"john",hasCar:!0,yearBought:2002},{name:"john",hasCar:!0,yearBought:2005},{name:"mary",hasCar:!0,yearBought:2015},{name:"john",hasCar:!0,yearBought:2018}];

const merged = owners.reduce((r, o) => {
  if(r[o.name])
    r[o.name].yearBought = [].concat(r[o.name].yearBought, o.yearBought)
  else
    r[o.name] = o;
  return r;
},{})

console.log(Object.values(merged))

0
Saurabh Agrawal On

Instead of using loadash you can achieve it using plain javascript.

Try this:

const owners = [{
  name: "john",
  hasCar: true,
  yearBought: 2002
}, {
  name: "john",
  hasCar: true,
  yearBought: 2005
}, {
  name: "mary",
  hasCar: true,
  yearBought: 2015
}, {
  name: "john",
  hasCar: true,
  yearBought: 2018
}]

let tmpList = [];
let result = [];
for (var i = 0; i < owners.length; i++) {
  if (tmpList.indexOf(owners[i].name) == -1) {
    tmpList.push(owners[i].name);
    result.push(owners[i]);
    result[result.length-1].yearBought = [result[result.length-1].yearBought];
    for (var j = 0; j < owners.length; j++) {
      if (i != j && owners[i].name == owners[j].name) {
        result[result.length-1].yearBought.push(owners[owners.length-1].yearBought);
      }
    }
  }
}
console.log(result);

0
Jack Bashford On

Just use reduce:

const owners = [{name:"john",hasCar:true,yearBought:2002},{name:"john",hasCar:true,yearBought:2005},{name:"mary",hasCar:true,yearBought:2015},{name:"john",hasCar:true,yearBought:2018}];

const newOwners = Object.values(owners.reduce((acc, curr) => {
  acc[curr.name] = acc[curr.name] ? { ...acc[curr.name], yearBought: [].concat(acc[curr.name].yearBought, curr.yearBought) } : curr;
  return acc;
}, {}));

console.log(newOwners);

0
Murtaza Hussain On

I hope this help using PURE lodash functions.. it looks clean and readable.

var array = [{
  name: "john",
  hasCar: true,
  yearBought: 2002
}, {
  name: "john",
  hasCar: true,
  yearBought: 2005
}, {
  name: "mary",
  hasCar: true,
  yearBought: 2015
}, {
  name: "john",
  hasCar: true,
  yearBought: 2018
}]

function mergeNames(arr) {
  return Object.values(_.chain(arr).groupBy('name').mapValues((g) => (_.merge(...g, {
    yearBought: _.map(g, 'yearBought')
  }))).value());
}
console.log(mergeNames(array));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Thanks :)

0
Ori Drori On

You can group the items with a unique key (name in your case), then map the groups, and merge the items in each group:

const { flow, partialRight: pr, groupBy, map, mergeWith, concat, isUndefined } = _

const mergeDuplicates = (isCollected, key) => flow(
  pr(groupBy, key), // group by the unique key
  pr(map, group => mergeWith({}, ...group, 
    (o, s, k) => isCollected(k) && !isUndefined(o) ? concat(o, s) : s
  )) // merge each group to a new object
)

const owners = [{name:"john",hasCar:true,yearBought:2002},{name:"john",hasCar:true,yearBought:2005},{name:"mary",hasCar:!0,yearBought:2015},{name:"john",hasCar:true,yearBought:2018}]

const isCollected = key => key === 'yearBought'

const result = mergeDuplicates(isCollected, 'name')(owners)

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

And the lodash/fp version:

const { flow, groupBy, map, mergeAllWith, cond, nthArg, concat } = _

const mergeDuplicates = (isCollected, key) => flow(
  groupBy(key),
  map(mergeAllWith(cond([[
    flow(nthArg(2), isCollected),
    concat,
    nthArg(1)
  ]])))
)

const owners = [{name:"john",hasCar:!0,yearBought:2002},{name:"john",hasCar:!0,yearBought:2005},{name:"mary",hasCar:!0,yearBought:2015},{name:"john",hasCar:!0,yearBought:2018}]

const isCollected = key => key === 'yearBought'

const result = mergeDuplicates(isCollected, 'name')(owners)

console.log(result)
<script src='https://cdn.jsdelivr.net/g/[email protected](lodash.min.js+lodash.fp.min.js)'></script>