Search code examples
javascriptmultidimensional-arrayarray-difference

How can I compare objects in 2 arrays in Javascript & return the missing elements?


Is there a way to compare an incomplete array to the source of truth array & return the missing elements? The array consists of objects & some of the objects have an array of objects.

let incompleteArr =[
          {
            "name": "Extra Toppings (Small)",
            "modifier_options": [{
                    "name": "Corn",
                    "price": 0.75
                },
                {
                    "name": "Extra mozzarella",
                    "price": 0.9
                },
                {
                    "name": "Onion",
                    "price": 0.45
                }
            ]
        },
        {
            "name": "Extra Toppings (Large)",
            "modifier_options": [{
                    "name": "Corn",
                    "price": 1.2
                },
                {
                    "name": "Extra Mozzarella",
                    "price": 1.7
                }
            ]
        }
    ]
  }]

  let completeArr =[
            {
                "name": "Extra Toppings (Small)",
                "modifier_options": [
                    {
                        "name": "Corn",
                        "price": 0.75
                    },
                    {
                        "name": "Extra mozzarella",
                        "price": 1.2
                    },
                    {
                        "name": "Extra Anchovies",
                        "price": 0.7
                    }
                ]
            },
            {
                "name": "Extra Toppings (Large)",
                "modifier_options": [
                    {
                        "name": "Corn",
                        "price": 1.2
                    },
                    {
                        "name": "Extra mozzarella",
                        "price": 1.7
                    }
                ]
            },
            {
                "name": "Extra Burger Toppings (Small)",
                "modifier_options": [
                    {
                        "name": "Extra Onion",
                        "price": 0.5
                    },
                    {
                        "name": "Extra Tomato",
                        "price": 0.55
                    },
                    {
                        "name": "Extra Cheese",
                        "price": 0.65
                    },
                    {
                        "name": "Extra Mayo",
                        "price": 0.3
                    },
                    {
                        "name": "Extra Patty",
                        "price": 2.5
                    }
                ]
            },
            {
                "name": "Extra Burger Toppings (Medium)",
                "modifier_options": [
                    {
                        "name": "Extra Onion",
                        "price": 0.6
                    },
                    {
                        "name": "Extra Tomato",
                        "price": 0.65
                    },
                    {
                        "name": "Extra Cheese",
                        "price": 0.75
                    },
                    {
                        "name": "Extra Mayo",
                        "price": 0.4
                    },
                    {
                        "name": "Extra Patty",
                        "price": 2.9
                    }
                ]
            },
            {
                "name": "Extra Burger Toppings (Large)",
                "modifier_options": [
                    {
                        "name": "Extra Onion",
                        "price": 0.8
                    },
                    {
                        "name": "Extra Tomato",
                        "price": 0.9
                    },
                    {
                        "name": "Extra Cheese",
                        "price": 0.95
                    },
                    {
                        "name": "Extra Mayo",
                        "price": 0.6
                    },
                    {
                        "name": "Extra Patty",
                        "price": 3.5
                    }
                ]
            }
        ]

Desired result is to return an array of missing elements. For example the "name" Extra Toppings (Small) must exist in the incompleteArr & the completeArr. Then the "modifier_options" must also be compared. Any modifier_options not in the incompleteArr must be pushed into the array of missing elements.

I have tried this so far

let missingMod = gfMods.filter(mod => lvMods.every(mod2 => !mod2.name.includes(mod.name)))

The output as expected is the missing items BUT I am not getting the missing nested array items from the modifier_options object. I have an idea that I need to loop through the arrays checking that the "names" exist & then a second loop to check if the modifier_options all exist. This is where I have been stuck.

[{
name:"Extra Burger Toppings (Small)",
modifier_options: [{
name:"Extra Onion",
price:0.5},
{name:"Extra Tomato",
price:0.55},
{name:"Extra Cheese",
price:0.65},
{name:"Extra Mayo",
price:0.3},
{name:"Extra Patty",
price:2.5}
]},
{name:"Extra Burger Toppings (Medium)",
modifier_options: [{
name:"Extra Onion",
price:0.6},
{name:"Extra Tomato",
price:0.65},
{name:"Extra Cheese",
price:0.75},
{name:"Extra Mayo",
price:0.4},
{name:"Extra Patty",
price:2.9}]
},
{name:"Extra Burger Toppings (Large)",
modifier_options: [{
name:"Extra Onion",
price:0.8},
{name:"Extra Tomato",
price:0.9},
{name:"Extra Cheese",
price:0.95},
{name:"Extra Mayo",
price:0.6},
{name:"Extra Patty",
price:3.5}]
}]

Solution

  • This solution utilizes the .reduce(), .find(), .some(), and .filter() Array methods.

    // Reduces the complete array, to a list of missing values from
    // the incomplete array.
    const result = completeArr.reduce((prev, next) => {
      // Finds whether the object exists in the incomplete array.
      // _main will hold the matching object from the incomplete
      // array if found, or undefined otherwise.
      const _main = incompleteArr.find(obj => obj.name == next.name);
      
      // If it does find, stop here and check for missing
      // values inside modifier options.
      if(_main){
        // .filter to get the missing modifier objects from the 
        // incomplete array. Done by checking if a
        // modifier option of the complete array 
        // is NOT present in the incomplete array's
        // modifier options list.
        const _mod = next.modifier_options.filter(next_mod => !_main.modifier_options.some(main_mod => next_mod.name == main_mod.name));
        
        // Add _mod(containing missing modifier options) to
        // the accumulating array and return.
        return [...prev, {parent: next.name, modifier_options: _mod}];
      }
      
      // This next object doesn't exist in the incomplete 
      // array as it wasn't found, so add it to the accumulating
      // list of missing items.
      return [...prev, next];
    }, []);
    

    let incompleteArr = [
          {
            "name": "Extra Toppings (Small)",
            "modifier_options": [{
                    "name": "Corn",
                    "price": 0.75
                },
                {
                    "name": "Extra mozzarella",
                    "price": 0.9
                },
                {
                    "name": "Onion",
                    "price": 0.45
                }
            ]
        },
        {
            "name": "Extra Toppings (Large)",
            "modifier_options": [{
                    "name": "Corn",
                    "price": 1.2
                },
                {
                    "name": "Extra Mozzarella",
                    "price": 1.7
                }
            ]
        }
    ];
    
    let completeArr = [
        {
            "name": "Extra Toppings (Small)",
            "modifier_options": [
                {
                    "name": "Corn",
                    "price": 0.75
                },
                {
                    "name": "Extra mozzarella",
                    "price": 1.2
                },
                {
                    "name": "Extra Anchovies",
                    "price": 0.7
                }
            ]
        },
        {
            "name": "Extra Toppings (Large)",
            "modifier_options": [
                {
                    "name": "Corn",
                    "price": 1.2
                },
                {
                    "name": "Extra mozzarella",
                    "price": 1.7
                }
            ]
        },
        {
            "name": "Extra Burger Toppings (Small)",
            "modifier_options": [
                {
                    "name": "Extra Onion",
                    "price": 0.5
                },
                {
                    "name": "Extra Tomato",
                    "price": 0.55
                },
                {
                    "name": "Extra Cheese",
                    "price": 0.65
                },
                {
                    "name": "Extra Mayo",
                    "price": 0.3
                },
                {
                    "name": "Extra Patty",
                    "price": 2.5
                }
            ]
        },
        {
            "name": "Extra Burger Toppings (Medium)",
            "modifier_options": [
                {
                    "name": "Extra Onion",
                    "price": 0.6
                },
                {
                    "name": "Extra Tomato",
                    "price": 0.65
                },
                {
                    "name": "Extra Cheese",
                    "price": 0.75
                },
                {
                    "name": "Extra Mayo",
                    "price": 0.4
                },
                {
                    "name": "Extra Patty",
                    "price": 2.9
                }
            ]
        },
        {
            "name": "Extra Burger Toppings (Large)",
            "modifier_options": [
                {
                    "name": "Extra Onion",
                    "price": 0.8
                },
                {
                    "name": "Extra Tomato",
                    "price": 0.9
                },
                {
                    "name": "Extra Cheese",
                    "price": 0.95
                },
                {
                    "name": "Extra Mayo",
                    "price": 0.6
                },
                {
                    "name": "Extra Patty",
                    "price": 3.5
                }
            ]
        }
    ];
    
    const result = completeArr.reduce((prev, next) => {
      const _main = incompleteArr.find(obj => obj.name == next.name);
      if(_main){
        const _mod = next.modifier_options.filter(next_mod => !_main.modifier_options.some(main_mod => next_mod.name == main_mod.name));
        return [...prev, {parent: next.name, modifier_options: _mod}];
      }
      return [...prev, next];
    }, []);
    
    console.log(result);

    Note: It'll return "Extra mozzarella" from "Extra Toppings (Large)" because of the case difference. Also, forgive the poor variable names, I hope this solution solves your problem.

    Update

    As per the request of OP, I added the .name of the parent object the missing modifier_options belong to.