merge partially duplicated arrays in javascript (l

2020-03-07 06:54发布

问题:

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

回答1:

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



回答2:

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



回答3:

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/lodash@4.17.11/lodash.min.js"></script>

Thanks :)



回答4:

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/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>