How to make an intersection between two arrays of objects

Viewed 161

I'm trying to do an intersection between two arrays of objects.

https://codesandbox.io/s/friendly-bohr-v73ob?fbclid=IwAR3yQDnftREENi8lF6wCKYE_F09pimlLgfYca0B_oIPqYYHvbAf4cvnG-n4

const list1 = [
  {
    name: "skinType",
    keys: [
      {
        id: "oily",
        label: "Oily"
      },
      {
        id: "dry",
        label: "Dry"
      }
    ]
  },
  {
    name: "finish",
    keys: [
      {
        id: "oily",
        label: "Oily"
      },
      {
        id: "dry",
        label: "Dry"
      },
      {
        id: "matte",
        label: "Matte"
      },
      {
        id: "natural",
        label: "Natural"
      },
      {
        id: "radiant",
        label: "Radiant / Glow"
      }
    ]
  },
  {
    name: "texture",
    keys: [
      {
        id: "matte",
        labal: "Matte"
      }
    ]
  }
];

const list2 = [
  {
    name: "skinType",
    keys: [
      {
        id: "oily",
        label: "Oily"
      },
      {
        id: "dry",
        label: "Dry"
      },
      {
        id: "gandac",
        label: "mazga"
      }
    ]
  },
  {
    name: "finish",
    keys: [
      {
        id: "oily",
        label: "Oily"
      }
    ]
  }
];

I came up with a solution but it only can do the intersection based on the name key from the object. Now I need now to do the intersection based on the id from the keys array.

const intersection = (list1, list2) => {
  return list2.filter(drp => list1.some(rect => rect.name === drp.name));
};

const result = intersection(react, drupal);

Expected result:

[
  {
    name: "skinType",
    keys: [
      {
        id: "oily",
        label: "Oily"
      },
      {
        id: "dry",
        label: "Dry"
      }
    ]
  },
  {
    name: "finish",
    keys: [
      {
        id: "oily",
        label: "Oily"
      }
    ]
  }
]
3 Answers

const react = [
  { name: "skinType", 
    keys: [
      { id: "oily", label: "Oily" },
      { id: "dry",  label: "Dry" }
    ]
  },
  { name: "finish", 
    keys: [
      { id: "oily", label: "Oily" },
      { id: "dry",  label: "Dry" },
      { id: "matte", label: "Matte" },
      { id: "natural", label: "Natural" },
      { id: "radiant", label: "Radiant / Glow" }
    ]
  },
  { name: "texture", 
    keys: [
      { id: "matte", labal: "Matte" }
    ]
  }
];

const drupal = [
  { name: "skinType", 
    keys: [
      { id: "oily", label: "Oily" },
      { id: "dry",  label: "Dry" },
      { id: "gandac", label: "mazga" }
    ]
  },
  { name: "finish", 
    keys: [
      { id: "oily", label: "Oily" }
    ]
  }
];

var result = [];
for(var item1 of react)
  for(var item2 of drupal)
    if(item1.name == item2.name){
      var keys = item2.keys.filter(x => item1.keys.some(y => y.id === x.id));
      result.push({name: item1.name, keys})      
      break;
    }  
console.log(result);

Do you care about repeats?

const array1 = [1,2,3,4];
const array2 = [5,6,7,8];

const union = (a1, a2) => [...a1, ...a2];

console.log(union(array1, array2));

If you don't want repeats

const array1 = [1,2,3,4,5];
const array2 = [4,5,6,7,8];


const distinct = (array) =>
  array
    ? array.reduce((results, item) => {
        if (!results.some((i) => i === item)) {
          results.push(item);
        }
        return results;
      }, [])
    : array;
    
const union = (a1, a2) => distinct([...a1, ...a2]);

console.log(union(array1, array2));

Another approach would be to avoid repeated values in the union result by first reducing the arrays to a mapping (via the key.id criteria), and then extracting the values of the mapping via Object.values() to obtain the union as an array of values.

The idea behind the intermediary mapping stage is to ensure that values in the resulting union are unique by the key.id.

In solution in code can look like this:

const react=[{name:"skinType",keys:[{id:"oily",label:"Oily"},{id:"dry",label:"Dry"}]},{name:"finish",keys:[{id:"oily",label:"Oily"},{id:"dry",label:"Dry"},{id:"matte",label:"Matte"},{id:"natural",label:"Natural"},{id:"radiant",label:"Radiant / Glow"}]},{name:"texture",keys:[{id:"matte",labal:"Matte"}]}];const drupal=[{name:"skinType",keys:[{id:"oily",label:"Oily"},{id:"dry",label:"Dry"},{id:"gandac",label:"mazga"}]},{name:"finish",keys:[{id:"oily",label:"Oily"}]}];

/* Gather both arrays into a combined array via concat() and reduce() the result 
to obtain the union. The reduce() produces a mapping, and we obtain the union array
via Object.values() to extract the map's values as an array */
const union = Object.values([].concat(drupal, react).reduce((map, item) => {

  /* For each item of the keys sub-array, update the map with value "key" at key 
  "key.id" given that key.id is the union criteria */
  item.keys.forEach(key => {
    map[key.id] = key;
  })

  return map;
}, {}))


console.log(union);

Related