1

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

0

2 Answers 2

2

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.

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

2 Comments

thank you for ur reply, but for the first solution, the problem is I need an object as a title because it would be other information so title.title wont work, and for the second answer it's working but as u said might be overkill
It's overkill for the case where you only have the single title property in the object. If you have more properties and none of them are unique. object-hash might be a fair solution.
1

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

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.