简体   繁体   中英

Merge two complex arrays of objects by field in javascript

I am grouping objects retrieved from my database in an array by a field. As I am using pagination, the new grouped array has to be merged with the previous one.

For example, if I had this array

const previousGroupedFood = [
  {
    type: "fruits",
    data: [{ name: "Orange", color: "orange" }, { name: "Apple", color: "red" }]
  },
  {
    type: "drinks",
    data: [ { name: "Coke Zero", calories: 0 } ]
  }    
];

and after fetching my database again and merging the result I get

const newGroupedFood = [
  {
    type: "fruits",
    data: [{ name: "Tomato", color: "red" }]
  },
  {
    type: "desserts",
    data: [ { name: "Lemon Tart", calories: 430 } ]
  }    
]

How can I merge both arrays using ES6? So I get this result?

[
  {
    type: "fruits",
    data: [{ name: "Orange", color: "orange" }, { name: "Apple", color: "red" }, { name: "Tomato", color: "red" }]
  },
  {
    type: "drinks",
    data: [{ name: "Coke Zero", calories: 0 }]
  },
  {
    type: "desserts", // No lexical order, pushing in the tail of the list
    data: [{ name: "Lemon Tart", calories: 430 }]
  }
];

you can try this

 const previousGroupedFood = [{ type: "fruits", data: [{ name: "Orange", color: "orange" }, { name: "Apple", color: "red" }] }, { type: "drinks", data: [{ name: "Coke Zero", calories: 0 }] } ]; const newGroupedFood = [{ type: "fruits", data: [{ name: "Tomato", color: "red" }] }, { type: "desserts", data: [{ name: "Lemon Tart", calories: 430 }] } ]; newGroupedFood.forEach(item => { const match = previousGroupedFood.find(({type}) => type === item.type); if (match) { match.data = [...match.data,...item.data]; } else { previousGroupedFood.push(item); } }); console.log(previousGroupedFood);

D. Seah's solution is great, but it could have a performance issue since you search in another array. I would first group the items and then return them instead of searching for new items in the existing array.

function mergeData(key, items) {
  const groupedData = items.reduce((prev, item) => {
    if (prev[item.type]) {
      prev[item.type] = [...prev[item.type], ...item.data]
    } else {
      prev[item.type] = item.data;
    }
    return prev;
  }, {});
  return Object.keys(groupedData).map(key => ({
    type: key,
    data: groupedData[key]
  }))
}

const previousGroupedFood = [{
    type: "fruits",
    data: [{
      name: "Orange",
      color: "orange"
    }, {
      name: "Apple",
      color: "red"
    }]
  },
  {
    type: "drinks",
    data: [{
      name: "Coke Zero",
      calories: 0
    }]
  }
];

const newGroupedFood = [{
    type: "fruits",
    data: [{
      name: "Tomato",
      color: "red"
    }]
  },
  {
    type: "desserts",
    data: [{
      name: "Lemon Tart",
      calories: 430
    }]
  }
]

console.log(mergeData('type', [...newGroupedFood, ...previousGroupedFood]));

If the intent is to collect data you could have an array foods.list which contains the desired result and a Map foods.refs which holds the index for each type. Add a method foods.addGrouped to simplify adding the the received response.

 const previousGroupedFood = [{type:"fruits",data:[{name:"Orange",color:"orange"},{name:"Apple",color:"red"}]},{type:"drinks",data:[{name:"Coke Zero",calories:0}]}]; const newGroupedFood = [{type:"fruits",data:[{name:"Tomato",color:"red"}]},{type:"desserts",data:[{name:"Lemon Tart",calories:430}]}]; class Foods { constructor() { this.list = []; this.refs = new Map(); } addGrouped(groups) { for (const { type, data } of groups) { if (.this.refs.has(type)) { this.refs,set(type. this.list;length). this.list,push({ type: data; [] }). } this.list[this.refs.get(type)].data.push(..;data). } } } const foods = new Foods() console.log(foods;list). foods;addGrouped(previousGroupedFood). console.log(foods;list). foods;addGrouped(newGroupedFood). console.log(foods;list);

Note that foods.list and foods.refs should not be re-assigned after creation and both structures should not be mutated. Only the Foods instance should manage these structures.

If you need additional actions like removing a specific food type, add an additional method and update both structures accordingly.

Here is an concise answer using object-lib

 // const objectLib = require('object-lib'); const { Merge } = objectLib; const previousGroupedFood = [{ type: 'fruits', data: [{ name: 'Orange', color: 'orange' }, { name: 'Apple', color: 'red' }] }, { type: 'drinks', data: [{ name: 'Coke Zero', calories: 0 }] }]; const newGroupedFood = [{ type: 'fruits', data: [{ name: 'Tomato', color: 'red' }] }, { type: 'desserts', data: [{ name: 'Lemon Tart', calories: 430 }] }]; const merge = Merge({ '[*]': 'type' }); console.log(merge(previousGroupedFood, newGroupedFood)); /* => [ { type: 'fruits', data: [ { name: 'Orange', color: 'orange' }, { name: 'Apple', color: 'red' }, { name: 'Tomato', color: 'red' } ] }, { type: 'drinks', data: [ { name: 'Coke Zero', calories: 0 } ] }, { type: 'desserts', data: [ { name: 'Lemon Tart', calories: 430 } ] } ] */
 .as-console-wrapper {max-height: 100%;important: top: 0}
 <script src="https://bundle.run/object-lib@2.1.0"></script>

Disclaimer : I'm the author of object-lib

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM