Reputation: 88
I am working on a project that I fetch data from the server and every data must be like this format
const DATA = [
{
title: {title: 'Main dishes'},
data: [
{_id: 1, type: 'Pizza'},
{_id: 2, type: 'Burger'},
{_id: 3, type: 'Risotto'},
],
},
{
title: {title: 'Sides'},
data: [
{_id: 4, type: 'French Fries'},
{_id: 5, type: 'Onion Rings'},
{_id: 6, type: 'Fried Shrimps'},
],
},
];
but my problem is sometimes ill get the same title object within two or three fetches so I need to merge between them and hide duplicated data I am using this function
merged = (data1, data2) => {
let arr = data1.concat(data2);
let titles = new Map(arr.map(({title}) => [title, new Map()]));
arr.forEach(({title, data}) => {
let map = titles.get(title);
data.forEach(o => map.set(o._id, o));
});
return Array.from(titles.entries(), ([title, map]) => ({
title,
data: [...map.values()],
}));
};
but the problem is this function only work if the title in the object is a string
example :
const DATA = [
{
title: {title: 'Main dishes'},
data: [
{_id: 1, type: 'Pizza'},
{_id: 2, type: 'Burger'},
{_id: 3, type: 'Risotto'},
],
},
{
title: {title: 'Sides'},
data: [
{_id: 4, type: 'French Fries'},
{_id: 5, type: 'Onion Rings'},
{_id: 6, type: 'Fried Shrimps'},
],
},
];
const DATA2 = [
{
title: {title: 'Sides'},
data: [{_id: 7, type: 'Lemon'}],
},
{
title: {title: 'Drinks'},
data: [
{_id: 8, type: 'Water'},
{_id: 9, type: 'Coke'},
{_id: 10, type: 'Beer'},
],
},
{
title: {title: 'Desserts'},
data: [
{_id: 11, type: 'Cheese Cake'},
{_id: 12, type: 'Ice Cream'},
],
},
];
must give me an array like this
data = [
{
title: {title: 'Main dishes'},
data: [
{_id: 1, type: 'Pizza'},
{_id: 2, type: 'Burger'},
{_id: 3, type: 'Risotto'},
],
},
{
title: {title: 'Sides'},
data: [
{_id: 4, type: 'French Fries'},
{_id: 5, type: 'Onion Rings'},
{_id: 6, type: 'Fried Shrimps'},
{_id: 7, type: 'Lemon'},
],
},
{
title: {title: 'Drinks'},
data: [
{_id: 8, type: 'Water'},
{_id: 9, type: 'Coke'},
{_id: 10, type: 'Beer'},
],
},
{
title: {title: 'Desserts'},
data: [
{_id: 11, type: 'Cheese Cake'},
{_id: 12, type: 'Ice Cream'},
],
},
]
so can anyone propose me a solution how to edit that function ? and thank you
Upvotes: 1
Views: 65
Reputation: 15530
You may leverage Array.prototype.reduce()
together with Map
:
const DATA=[{title:{title:"Main dishes"},data:[{_id:1,type:"Pizza"},{_id:2,type:"Burger"},{_id:3,type:"Risotto"}]},{title:{title:"Sides"},data:[{_id:4,type:"French Fries"},{_id:5,type:"Onion Rings"},{_id:6,type:"Fried Shrimps"}]}],
DATA2=[{title:{title:"Sides"},data:[{_id:7,type:"Lemon"}]},{title:{title:"Drinks"},data:[{_id:8,type:"Water"},{_id:9,type:"Coke"},{_id:10,type:"Beer"}]},{title:{title:"Desserts"},data:[{_id:11,type:"Cheese Cake"},{_id:12,type:"Ice Cream"}]}]
const merged = [
...[...DATA, ...DATA2]
.reduce((acc, item) => {
const key = item.title.title
const group = acc.get(key)
if(group){
const dataToInsert = item
.data
.filter(({_id: id}) =>
!group
.data
.map(({_id}) => _id)
.includes(id))
group.data.push(...dataToInsert)
} else {
acc.set(key, item)
}
return acc
}, new Map)
.values()
]
console.log(merged)
.as-console-wrapper{min-height: 100%}
Upvotes: 1
Reputation: 4205
That's because each {title: 'foo-bar'}
object is checked by reference when you do titles.get(title)
and not by their value.
Either save the string titles in the titles
map as keys, or use something like object-hash
to turn objects into identifiers.
Using the string keys:
merged = (data1, data2) => {
let arr = data1.concat(data2);
let titles = new Map(arr.map(({title}) => [title.title, new Map()])); // notice the `title.title` instead of `title`
arr.forEach(({title, data}) => {
let map = titles.get(title.title); // `title.title` instead of `title`
data.forEach(o => map.set(o._id, o));
});
return Array.from(titles.entries(), ([title, map]) => ({
title,
data: [...map.values()],
}));
};
and using object-hash
:
import hash from 'object-hash';
merged = (data1, data2) => {
let arr = data1.concat(data2);
let titles = new Map(arr.map(({title}) => [hash(title), new Map()]));
arr.forEach(({title, data}) => {
let map = titles.get(hash(title));
data.forEach(o => map.set(o._id, o));
});
return Array.from(titles.entries(), ([title, map]) => ({
title,
data: [...map.values()],
}));
};
Note that hashing the objects might be overkill in this case. I think the string solution is enough.
Upvotes: 2