captain_watanabe
captain_watanabe

Reputation: 119

Merging two arrays without duplicates

I've been stuck on this problem for a few days now.

let data1 = [{
    project_code: "110",
    project_text: "SampleProject1",
    location: "Seattle",
    startingCost: 0,
    actualCost: 399.99
},
{
    project: "110",
    project_text: "SampleProject1",
    location: "Bellevue",
    startingCost: 0,
    actualCost: 599.99
}];



let data2 = [{
        project: "110",
        project_text: "SampleProject1",
        location: "Seattle",
        startingCost: 249.99,
        actualCost: ""
    },
    {
        project: "110",
        project_text: "SampleProject1",
        location: "Bellevue",
        startingCost: 699.99,
        actualCost: ""
    },
    {
        project: "110",
        project_text: "SampleProject1",
        location: "North Gate",
        startingCost: 899.99,
        actualCost: 1199.99
    }]

The end goal here is that i want it to be merged into one array and the values should be updated like this:

let output = [{
    project: "110",
    project_text: "SampleProject1",
    location: "Seattle",
    startingCost: 249.99, // FROM DATA2
    actualCost: 399.99 // FROM DATA1
},
{
    project: "110",
    project_text: "SampleProject1",
    location: "Bellevue",
    startingCost: 699.99, // FROM DATA2
    actualCost: 599.99 // FROM DATA1
},
{
    // THIS ONE IS ADDING IN NEW DATA
    project: "110",
    project_text: "SampleProject1",
    location: "North Gate",
    startingCost: 899.99,
    actualCost: 1199.99
},

]

I would much prefer a vanilla JS approach but i'll be ok with Lodash as long as i get the output closer to that.

Upvotes: 1

Views: 195

Answers (3)

Nitheesh
Nitheesh

Reputation: 19986

Try using Array.reduce.

Logic

  • Loop through data2 with Array.reduce.
  • Check the matching node in data1 from data2.
  • If matching node found, merge the startingCost and actualCost.
  • Even though this covers the requirement, dont stop here. You have to verify each nodes in data1 has already been covered in result. If that is not covered, push the missing node to result.
    const data1 = [{
      project: "110",
      project_text: "SampleProject1",
      location: "Seattle",
      startingCost: 0,
      actualCost: 399.99
    },
    {
      project: "110",
      project_text: "SampleProject1",
      location: "Bellevue",
      startingCost: 0,
      actualCost: 599.99
    }];
    
    const data2 = [{
      project: "110",
      project_text: "SampleProject1",
      location: "Seattle",
      startingCost: 249.99,
      actualCost: ""
    },
    {
      project: "110",
      project_text: "SampleProject1",
      location: "Bellevue",
      startingCost: 699.99,
      actualCost: ""
    },
    {
      project: "110",
      project_text: "SampleProject1",
      location: "North Gate",
      startingCost: 899.99,
      actualCost: 1199.99
    }];
    
    const result = data2.reduce((acc, curr) => {
      const matchingNode = data1.find((node) =>
        node.project === curr.project &&
        node.project_text === curr.project_text &&
        node.location === curr.location);
      if(matchingNode) {
        const updatedNode = { ...matchingNode }; // Take a copy of matching node
        updatedNode.startingCost = matchingNode.startingCost || curr.startingCost;
        updatedNode.actualCost = matchingNode.actualCost || curr.actualCost;
        acc.push(updatedNode);
      } else {
        acc.push(curr);
      }
      return acc;
    }, []);
    
    // Since we have looped through data2 to generate the array,
    // we have to ensure that we have not missed any node from data1 aswell
    data1.forEach((node) => {
      const matchingNode = result.find((resultNode) =>
        node.project === resultNode.project &&
        node.project_text === resultNode.project_text &&
        node.location === resultNode.location);
      if (!matchingNode) {
        result.push(node);
      }
    })
    
    console.log(result);

Upvotes: 0

Rinshan Kolayil
Rinshan Kolayil

Reputation: 1139

let data1 = [{project_code: "110",project_text: "SampleProject1",location: "Seattle",startingCost: 0,actualCost: 399.99},{project: "110",project_text: "SampleProject1",location: "Bellevue",startingCost: 0,actualCost: 599.99}];
let data2 = [{project: "110",project_text: "SampleProject1",location: "Seattle",startingCost: 249.99,actualCost: ""},{project: "110",project_text: "SampleProject1",location: "Bellevue",startingCost: 699.99,actualCost: ""},{project: "110",project_text: "SampleProject1",location: "North Gate",startingCost: 899.99,actualCost: 1199.99}]
data2.forEach((obj) => {
    let ob = data1.find((e) => e.location == obj.location);
    // YOU CAN ADD OTHER CONDTIONS HERE WITH && OPERATOR
    if (ob !== undefined) {
        obj.actualCost = ob.actualCost;
    }
});
console.log(data2);

The above code will override data2.

let data1 = [{project_code: "110",project_text: "SampleProject1",location: "Seattle",startingCost: 0,actualCost: 399.99},{project: "110",project_text: "SampleProject1",location: "Bellevue",startingCost: 0,actualCost: 599.99}];
let data2 = [{project: "110",project_text: "SampleProject1",location: "Seattle",startingCost: 249.99,actualCost: ""},{project: "110",project_text: "SampleProject1",location: "Bellevue",startingCost: 699.99,actualCost: ""},{project: "110",project_text: "SampleProject1",location: "North Gate",startingCost: 899.99,actualCost: 1199.99}]
var final_result = [];
data2.forEach(({...obj}) => {
    let ob = {...data1.find((e) => e.location == obj.location)};
    //{...data}; used to Clone Object without reference to do not override data1
    if (Object.keys(ob).length !== 0) {
        ob.startingCost = obj.startingCost;
        final_result.push(ob);
    } else {
        final_result.push(obj);
    }
});
console.log(final_result);

  1. {...obj} in the forleach (forEach(({...obj})) used to restrict modifying data2 object when modifying final_result

Upvotes: 2

rahulg510
rahulg510

Reputation: 63

This is how I would do it. Like the comments suggested, which property is suppose to be unique? It would be the criteria I would use to find the data in the other array, I used location because that is the only thing that seemed to be different. You can of course use more than one property to find a match like: item.project === obj.project && item.location === obj.location

data2.forEach(obj=>{
        let object1 = data1.find(item=>item.location === obj.location);
        if(object1){
            object1.startingCost = object1.startingCost || obj.startingCost;
            object1.actualCost = object1.actualCost || obj.actualCost;
        }
        else{
            data1.push(obj);
        }

})

This would default to keeping the data from data1 array if both arrays have data for an object. So change it around for what your program needs are.

Upvotes: 0

Related Questions