0

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 }]
  }
];
2
  • Are the data items unique in each page or could they be duplicate? What do you want to see when there is { name: "Tomato", color: "red" } in third page for fruits? Commented Jan 19, 2021 at 22:54
  • @hurricane Unique data. I am rendering the items in a section list, in my real use case I have uris to the items and other data. Commented Jan 19, 2021 at 22:55

4 Answers 4

3

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

Sign up to request clarification or add additional context in comments.

1 Comment

Agree with this answer. To use specifically es6 syntax you can replace the concat with the spread operator. A simple example would be [...array1, ...array2]
1

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

Comments

1

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.

Comments

0

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/[email protected]"></script>

Disclaimer: I'm the author of object-lib

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.