3

I have two arrays as follows:

array1 = [
         {id:1, children: ['a', 'b']},
         {id:2, children: ['a', 'b']},
         {id:3, children: ['b', 'c']},
         {id:4, children: ['c', 'a']},
         {id:5, children: ['a', 'b', 'c']}];

array2 = ['a', 'b'];

Now I want to write a code in JS/TS which will find the exact objects from array1 where every element of children array from array 2 matches exactly with every elements of children array from array 1 (Order doesn't matter).

I have tried to solve this problem with three filters with additional condition of length matching of children array between array 1 and array2. But this also picks up if at least one element gets matched of those children array with desired array length.

I would really appreciate if someone gives me the solution.

array1
.filter(a => a.children
.filter(b => array2
.filter(c => b === c)).length === array2.length);

Edit:

I had actually simplified the problem a bit in the above example. In my actual project, the the two arrays are as follows:

 const productOrders: ProductOrder[] =
      [
        {
          productId: 1, subProductOrders:
            [{subProduct: {subProductId: 1}}, {subProduct: 
{subProductId: 2}}]
        },
        {
          productId: 1, subProductOrders:
            [{subProduct: {subProductId: 2}}, {subProduct: 
{subProductId: 1}}]
        },
        {
          productId: 1, subProductOrders:
            [{subProduct: {subProductId: 2}}, {subProduct: 
{subProductId: 3}}]
        },
        {
          productId: 1, subProductOrders:
            [{subProduct: {subProductId: 1}}, {subProduct: 
{subProductId: 2}}, {subProduct: {subProductId: 3}}]
        },
      ];

    const matchingCriteria: SubProductOrder[] =
      [
        [{subProduct: {subProductId: 1}}, {subProduct: {subProductId: 
2}}]

      ];

Now I want to find the products from the productOrders array where subProductId of the subProductOrders array matches with the subProductId of the matchingCriteria Array (Order doesn't matter). In the above example, the first two products of the productOrders Array should match despite unordered subProductsIds

javaland235
  • 743
  • 3
  • 11
  • 21
  • Check out this question for a detailed answer - https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript – paulosullivan22 Oct 27 '19 at 13:54

4 Answers4

4

You could take a Set and check the children against this structure.

var array1 = [{ id: 1, children: ['a', 'b'] }, { id: 2, children: ['a', 'b'] }, { id: 3, children: ['b', 'c'] }, { id: 4, children: ['c', 'a'] },  { id: 5, children: ['a', 'b', 'c'] }],
    array2 = ['a', 'b'],
    set2 = new Set(array2),
    result = array1.filter(({ children }) =>
        children.length === set2.size && children.every(Set.prototype.has, set2));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

For a more complex data structure, you could destructure the needed parts and check against a set of subProductId.

const
    getId = ({ subProduct: { subProductId } }) => subProductId;

var productOrders = [{ productId: 1, subProductOrders: [{ subProduct: { subProductId: 1 } }, { subProduct: { subProductId: 2 } }] }, { productId: 1, subProductOrders: [{ subProduct: { subProductId: 2 } }, { subProduct: { subProductId: 1 } }] }, { productId: 1, subProductOrders: [{ subProduct: { subProductId: 2 } }, { subProduct: { subProductId: 3 } }] }, { productId: 1, subProductOrders: [{ subProduct: { subProductId: 1 } }, { subProduct: { subProductId: 2 } }, { subProduct: { subProductId: 3 } }] }],
    matchingCriteria = [{ subProduct: { subProductId: 1 } }, { subProduct: { subProductId: 2 } }],
    set2 = new Set(matchingCriteria.map(getId)),
    result = productOrders.filter(({ subProductOrders }) =>
        subProductOrders.length === set2.size &&
        subProductOrders.every(o => set2.has(getId(o)))
    );

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • sorry I had simplified the example a bit too much. I have added an edit section. Can you please give me an updated solution. – javaland235 Oct 27 '19 at 16:49
  • what happens to other items of `matchingCriteria`, woulkd it not be better to have only one array without a nested array? – Nina Scholz Oct 27 '19 at 17:16
  • fantastic. This worked like charm. To answer your previous question, I only need the some specific subProductOrders (with subProductId) to consolidate some products with the matching subProductOrders. So i have made the matching criteria array with only subProductOrders for this purpose. – javaland235 Oct 27 '19 at 17:57
  • The purpose of this coding is, in a POS system the user gets to add several subProducts(topping/Add-on etc.) of a particular product(e.g. Cappuccino). User gets to add one/multiple toppings/add-ons for each unit of a cappuccino cup. So the purpose here is to consolidate the units with same add-ons and calculate the qty and total price. That's pretty much it – javaland235 Oct 27 '19 at 18:01
1

You can use Array#every() and length in a single filter

const arr1 = [
   {id:1, children: ['a', 'b']},
   {id:2, children: ['a', 'b']},
   {id:3, children: ['b', 'c']},
   {id:4, children: ['c', 'a']},
   {id:5, children: ['a', 'b', 'c']}
];


const arr2 = ['a', 'b'];

const matched = arr1.filter(({children: c}) => c.length === arr2.length && arr2.every(v => c.includes(v)))

console.log(matched)
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • 1
    consider `arr2 = abb`, `children=abX` – georg Oct 27 '19 at 14:14
  • @georg Right...only works when `arr2` values are unique. Not clear from OP if that is true or not. Would be same issue if using `Set` – charlietfl Oct 27 '19 at 14:25
  • @ charlietfl sorry I had simplified the example a bit too much. I have added an edit section. Can you please give me an updated solution. – javaland235 Oct 27 '19 at 16:47
0

array1 = [
         {id:1, children: ['a', 'b']},
         {id:2, children: ['b', 'a']},
         {id:3, children: ['b', 'c']},
         {id:4, children: ['c', 'a']},
         {id:5, children: ['a', 'b', 'c']}];

array2 = ['a', 'b'];

const x = array1
  .map(a => a.children.sort())
  .filter(a => a.length === array2.length)
  .map(a => a.sort())
  .filter(a => JSON.stringify(a)==JSON.stringify(array2))

console.log(x)
Jacek Rojek
  • 1,082
  • 8
  • 16
-1

You are filtering array1 elements based on the equality of element.children to array2. so i encourage you to take a look at these answers. as it discuss the equality of two arrays in javascript.

and modify the following code to your needs, this is just one of the easiest options available:

array1.filter((element,index) => {
      return JSON.stringify(element.children) === JSON.stringify(array2)
})
Alan Omar
  • 4,023
  • 1
  • 9
  • 20
  • Using JSON.stringify only works if order is identical – charlietfl Oct 27 '19 at 14:27
  • I modified my answer to point out that this is one of the many options available for the equality of two arrays. – Alan Omar Oct 27 '19 at 14:32
  • OK...but OP already stated order is not important so this approach not relevant in this situation – charlietfl Oct 27 '19 at 14:33
  • How about return JSON.stringify(element.children.sort()) === JSON.stringify(array2.sort()) would this also fail in this case? – Alan Omar Oct 27 '19 at 16:04
  • Not very efficient and the sort may introduce other issues if order needs to be maintained. Note that if you did use this approach the `JSON.stringify(array2.sort())` doesn't need to be performed every iteration. Do it once and store result – charlietfl Oct 27 '19 at 16:06
  • Yes you are wright, javascript never fails to surprise me. i thought the sort is not a mutating method but it turns out to be a one. – Alan Omar Oct 27 '19 at 16:11
  • Hi @charlietfl sorry to bother you, but one last comment what do you think of this code: return JSON.stringify([...element.children.join("")].sort()) === JSON.stringify([...array2.join()].sort()) this will not mutate the arrays but i don't know how efficient will be? – Alan Omar Oct 27 '19 at 16:28
  • Just keeps getting more expensive – charlietfl Oct 27 '19 at 16:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201469/discussion-between-alan-jouhar-and-charlietfl). – Alan Omar Oct 27 '19 at 16:36