1

Is there a way to tell if an object array has any common elements to another object array, and what that object intersect is? (like a Contains function). In the example below,ProductId3 in Object Array 1, is also contained in Object Array 2.

I'm thinking of using a double for loop . However is there a more efficient/optimal way, or shorthand ecma or lodash function?

We are checking all object members, not just ProductId.

array1.forEach(arr1 => {
  array2.forEach(arr2 => { 
       if (arr1.productId === arr2.productId && 
           arr1.productName === arr2.productName ...

Object Array 1:

[
{
    ProductId: 50,
    ProductName: 'Test1',
    Location: 77,
    Supplier: 11,
    Quantity: 33
},
{
    ProductId: 3,
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25
}
]

Object Array 2:

[
{
    ProductId: 1,
    ProductName: 'ABC',
    Location: 3,
    Supplier: 4,
    Quantity: 52
},
{
    ProductId: 2,
    ProductName: 'DEF',
    Location: 1,
    Supplier: 2,
    Quantity: 87
},
{
    ProductId: 3,
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25
},
{
    ProductId: 4,
    ProductName: 'XYZ',
    Location:  5,
    Supplier: 6,
    Quantity: 17
}
]

Resources:

How to determine if Javascript array contains an object with an attribute that equals a given value?

Javascript: Using `.includes` to find if an array of objects contains a specific object

mattsmith5
  • 540
  • 4
  • 29
  • 67

6 Answers6

2

If we can assume that each array's elements (we will call them sub-dictionaries) contain exactly the same keys in the same order, then this is my idea:

  1. Convert each array into a new array whose elements are the JSON representations of the original sub-dictionaries values. This is an o(N) operation performed twice.
  2. Of the new, converted arrays find the shortest one. Convert the other into a set. This is also o(N).
  3. For each element of the shorter converted array, check to see if the set contains this value. This is also o(N).

let arr1 = [
{
    ProductId: 50,
    ProductName: 'Test1',
    Location: 77,
    Supplier: 11,
    Quantity: 33
},
{
    ProductId: 3,
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25
}
];

let arr2 = [
{
    ProductId: 1,
    ProductName: 'ABC',
    Location: 3,
    Supplier: 4,
    Quantity: 52
},
{
    ProductId: 2,
    ProductName: 'DEF',
    Location: 1,
    Supplier: 2,
    Quantity: 87
},
{
    ProductId: 3,
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25
},
{
    ProductId: 4,
    ProductName: 'XYZ',
    Location:  5,
    Supplier: 6,
    Quantity: 17
}
];

// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(arr));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(arr));});

// Find shortest array of JSON strings:
const l1 = arr1New.length;
const l2 = arr2New.length;
// enumerate shortest list
let list, set, l, arr;
if (l1 <= l2) {
    list = arr1New;
    set = new Set(arr2New);
    l = l1;
    arr = arr1;
}
else {
    list = arr2New;
    set = new Set(arr1New);
    l = l2;
    arr = arr2;
}

for(let i = 0; i < l; i++) {
    if (set.has(list[i])) {
        console.log(arr[i]);
    }
}

Update

If the sub-dictionary keys are not necessarily in order, then we have to create new sub-dictionaries from these where the keys are in order:

// Create function to create new dictionaries sorted by keys

function sort_dict(d) {
    items = Object.keys(d).map(function(key) {
        return [key, d[key]];
    });
    items.sort(function(first, second) {
        return first[0] < second[0] ? -1 : (first[0] > second[0] ? 1 : 0);
    });
    sorted_dict = {};
    items.forEach(function(x) {
        sorted_dict[x[0]] = x[1];
    });
    return(sorted_dict);
}

// And then we have these modified lines:
// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});

Modified Code

let arr1 = [
{
    ProductId: 50,
    ProductName: 'Test1',
    Location: 77,
    Supplier: 11,
    Quantity: 33
},
{
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25,
    ProductId: 3 // Not in the same order as the others
}
];

let arr2 = [
{
    ProductId: 1,
    ProductName: 'ABC',
    Location: 3,
    Supplier: 4,
    Quantity: 52
},
{
    ProductId: 2,
    ProductName: 'DEF',
    Location: 1,
    Supplier: 2,
    Quantity: 87
},
{
    ProductId: 3,
    ProductName: 'GHI',
    Location: 1,
    Supplier: 4,
    Quantity: 25
},
{
    ProductId: 4,
    ProductName: 'XYZ',
    Location:  5,
    Supplier: 6,
    Quantity: 17
}
];


function sort_dict(d) {
    items = Object.keys(d).map(function(key) {
        return [key, d[key]];
    });
    items.sort(function(first, second) {
        return first[0] < second[0] ? -1 : (first[0] > second[0] ? 1 : 0);
    });
    sorted_dict = {};
    items.forEach(function(x) {
        sorted_dict[x[0]] = x[1];
    });
    return(sorted_dict);
}

// Convert each sub-array's values to JSON string:
let arr1New = arr1.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});
let arr2New = arr2.map(function(arr) {return JSON.stringify(Object.values(sort_dict(arr)));});

// Find shortest array of JSON strings:
const l1 = arr1New.length;
const l2 = arr2New.length;
// enumerate shortest list
let list, set, l, arr;
if (l1 <= l2) {
    list = arr1New;
    set = new Set(arr2New);
    l = l1;
    arr = arr1;
}
else {
    list = arr2New;
    set = new Set(arr1New);
    l = l2;
    arr = arr2;
}

for(let i = 0; i < l; i++) {
    if (set.has(list[i])) {
        console.log(arr[i]);
    }
}
Booboo
  • 38,656
  • 3
  • 37
  • 60
  • A nice method but it will fail if the properties are not all in the same order. – kelsny Sep 30 '22 at 12:55
  • @caTS Yes, that is so and I have implied as much in my answer. I have updated the answer to handle the situation where the properties are not in the same order. – Booboo Sep 30 '22 at 13:56
  • this is great code , what are thoughts on Rohìt Jíndal answer above? – mattsmith5 Oct 01 '22 at 14:23
  • After a quick look it *appears* to me that his solution is efficient but only works if all properties of each object are in the same order. – Booboo Oct 01 '22 at 18:26
2

Is there a way to tell if an object array has any common elements to another object array ? - Yes you can achieve this with the help of Array.some() method. It returns true if, in the array, it finds an element for which the provided function returns true; otherwise it returns false.

const array1 = [{
  ProductId: 50,
  ProductName: 'Test1',
  Location: 77,
  Supplier: 11,
  Quantity: 33
}, {
  ProductId: 3,
  ProductName: 'GHI',
  Location: 1,
  Supplier: 4,
  Quantity: 25
}];

const array2 = [{
  ProductId: 1,
  ProductName: 'ABC',
  Location: 3,
  Supplier: 4,
  Quantity: 52
}, {
  ProductId: 2,
  ProductName: 'DEF',
  Location: 1,
  Supplier: 2,
  Quantity: 87
}, {
  ProductId: 3,
  ProductName: 'GHI',
  Location: 1,
  Supplier: 4,
  Quantity: 25
}, {
  ProductId: 4,
  ProductName: 'XYZ',
  Location:  5,
  Supplier: 6,
  Quantity: 17
}];

const isCommonProducts = array2.some(({ ProductId }) => array1.map(obj => obj.ProductId).includes(ProductId));

console.log(isCommonProducts);

Update : As per the author comment, we have to check all the properties of an object. Hence, we can achieve that by comparing the JSON string by converting the object into a string.

Live Demo :

const array1 = [{
  ProductId: 50,
  ProductName: 'Test1',
  Location: 77,
  Supplier: 11,
  Quantity: 33
}, {
  ProductId: 3,
  ProductName: 'GHI',
  Location: 1,
  Supplier: 4,
  Quantity: 25
}];

const array2 = [{
  ProductId: 1,
  ProductName: 'ABC',
  Location: 3,
  Supplier: 4,
  Quantity: 52
}, {
  ProductId: 2,
  ProductName: 'DEF',
  Location: 1,
  Supplier: 2,
  Quantity: 87
}, {
  ProductId: 3,
  ProductName: 'GHI',
  Location: 1,
  Supplier: 4,
  Quantity: 25
}, {
  ProductId: 4,
  ProductName: 'XYZ',
  Location:  5,
  Supplier: 6,
  Quantity: 17
}];

const getFilteredProducts = array2.filter(productObj => JSON.stringify(array1).indexOf(JSON.stringify(productObj)) !== -1);

console.log(getFilteredProducts);
Debug Diva
  • 26,058
  • 13
  • 70
  • 123
  • 1
    You can change "{ productID }" to just " product " within the parenthesis & do a check on all necessary pieces! ( using product.whateverPartOfTheObject ) – Amanda Sep 30 '22 at 23:56
  • @mattsmith5 Thanks for pointing that out! I updated the answer as per your requirement. Hope it will work as per your expectation now. – Debug Diva Oct 01 '22 at 12:41
0

For a simple yet reasonably fast solution, you can (1) use a Set of productIds from the first array, then (2) filter the second array based on the ids from the first one, this you only have to go over each array once O(n).

let arr1 = [
  {
    ProductId: 50,
    ProductName: "Test1",
    Location: 77,
    Supplier: 11,
    Quantity: 33,
  },
  {
    ProductId: 3,
    ProductName: "GHI",
    Location: 1,
    Supplier: 4,
    Quantity: 25,
  },
];

let arr2 = [
  {
    ProductId: 1,
    ProductName: "ABC",
    Location: 3,
    Supplier: 4,
    Quantity: 52,
  },
  {
    ProductId: 2,
    ProductName: "DEF",
    Location: 1,
    Supplier: 2,
    Quantity: 87,
  },
  {
    ProductId: 3,
    ProductName: "GHI",
    Location: 1,
    Supplier: 4,
    Quantity: 25,
  },
  {
    ProductId: 4,
    ProductName: "XYZ",
    Location: 5,
    Supplier: 6,
    Quantity: 17,
  },
];

const getCommonItems = (arr1, arr2) => {
  let firstIdSet = new Set(arr1.map((product) => product.ProductId)); //1
  return arr2.filter((product) => firstIdSet.has(product.ProductId)); //2
};

console.log(getCommonItems(arr1, arr2));
Abdellah Hariti
  • 657
  • 4
  • 8
-1

If you want a deep equality comparison(for nested objects or for all (key, value) pairs), I would suggest a slightly better approach which is using the base64 encoding/decoding to improve on comparison performance. So my approach is to:

  1. merge the arrays and convert the object to base64 strings.
  2. Group the recurrences together
  3. Filter on duplicates
  4. revert the base64 strings into their original object.

const convertObjToBase64 = o => btoa(JSON.stringify(o));
const convertBase64ToObj = str => JSON.parse(atob(str));
const arrayToObjCount = arr => arr.reduce((res, v) => {
  res[v] = (res[v] ?? 0) + 1;
  return res;
}, {});

const findDuplicateObjectsInMultipleArrays = (...arrays) => {
  const base64Array = Array.from(arrays.flat(), convertObjToBase64);
  const objCount = arrayToObjCount(base64Array);
  const duplicates = Object.entries(objCount).reduce((prev, [k, v]) => {
    if (v > 1) {
      prev.push(convertBase64ToObj(k));
    }
    return prev;
  }, []);
  return duplicates;
}

let arr1 = [{
    ProductId: 50,
    ProductName: 'Test1',
    Location: {
      LocationId: 77,
      LocationName: 'Location 77'
    },
    Supplier: 11,
    Quantity: 33
  },
  {
    ProductId: 3,
    ProductName: 'GHI',
    Location: {
      LocationId: 1,
      LocationName: 'Location 1'
    },
    Supplier: 4,
    Quantity: 25
  }
];

let arr2 = [{
    ProductId: 1,
    ProductName: 'ABC',
    Location: {
      LocationId: 3,
      LocationName: 'Location 3'
    },
    Supplier: 4,
    Quantity: 52
  },
  {
    ProductId: 2,
    ProductName: 'DEF',
    Location: {
      LocationId: 1,
      LocationName: 'Location 1'
    },
    Supplier: 2,
    Quantity: 87
  },
  {
    ProductId: 3,
    ProductName: 'GHI',
    Location: {
      LocationId: 1,
      LocationName: 'Location 1'
    },
    Supplier: 4,
    Quantity: 25
  },
  {
    ProductId: 4,
    ProductName: 'XYZ',
    Location: {
      LocationId: 5,
      LocationName: 'Location 5'
    },
    Supplier: 6,
    Quantity: 17
  }
];

let arr3 =[ 
  {
    ProductId: 2,
    ProductName: 'DEF',
    Location: {
      LocationId: 1,
      LocationName: 'Location 1'
    },
    Supplier: 2,
    Quantity: 87
  },
  {
    ProductId: 3,
    ProductName: 'GHI',
    Location: {
      LocationId: 2,
      LocationName: 'Location 2'
    },
    Supplier: 4,
    Quantity: 25
  },
   {
    ProductId: 4,
    ProductName: 'XYZ',
    Location: {
      LocationId: 6,
      LocationName: 'Location 5'
    },
    Supplier: 6,
    Quantity: 17
  }
];
console.log(findDuplicateObjectsInMultipleArrays(arr1, arr2, arr3));
Tarzan
  • 701
  • 3
  • 9
-1

I will post two solutions:

First Solution is readable one Code is not 100% performance optimized, but it is readable and elegant.

Playground link with working code

First, we need a method that compares two objects of any type. The method compares the first-level properties, so if we have nested object properties, it will compare them by reference.

const areTheSame = (a: any, b: any) => {
  const objAProps = Object.keys(a).filter(key => typeof a[key] !== "function")
  const objBProps = Object.keys(b).filter(key => typeof b[key] !== "function")

  if (objAProps.length !== objBProps.length) {
    return false;
  }

  return objAProps.every((propName) => a[propName] === b[propName]);
}

And then we can implement readable intersect method which will work for any array types:

const getIntersection = (array1: Array<any>, array2: Array<any>) => {
  return array1.filter(a1Item => array2.some(a2Item => areTheSame(a1Item, a2Item)));
}

The Second solution is performance-oriented, its drawback is that it is not so readable

First, we calculate the has for all objects, then within a single forEach loop we can identify the intersection based on that Hash. I have used md5, but any hash algorithm or library can be used.

Hers is stack blitz link playground. It can be run, ignore the import error.


const getArrayIntersection = (
  firstArray: Array<any>,
  secondArray: Array<any>
) => {
  const array1Hashed = firstArray.map((i) => md5(JSON.stringify(i)));
  const array2Set = new Set(secondArray.map((i) => md5(JSON.stringify(i))));

  const result: Array<any> = [];

  array1Hashed.forEach((itemHash, index) => {
    if (array2Set.has(itemHash)) {
      result.push(firstArray[index]);
    }
  });
  return result;
};


Svetoslav Petkov
  • 1,117
  • 5
  • 13
-1

Just to piggyback @Rohìt Jíndal, you can check if an array has a specific object like so:

const resultObj = arr1.filter(obj => obj.id=== "whatever" && obj.productname == "whatever") // ETC ETC
Amanda
  • 239
  • 1
  • 5