Home > database >  NodeJs: JSON Array/Object merging not working
NodeJs: JSON Array/Object merging not working

Time:03-29

Having an issue with merging nested JSON Array objects.

var o1 = {
    "customerId": "ABC",        
    "questions": [
        {
        "status": 2,
        "isBookmarked": 0,
        "questionId": 1 
        }
      ]      
    }
   

 var o2 = {
  "customerId": "ABC",    
  "questions": [
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 2    
      },
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 3    
      }
    ]   
  }

The question array needs to merge together. If the same question is found in both objects then it should take 2 object values (o2 - object).

I tried to do it like this:

const o3 = Object.assign({}, o1, o2);

Here is the link to reproduce - https://jsfiddle.net/y97afosd/

Expected output -

{
  "customerId": "ABC",    
  "questions": [
            {
        "status": 2,
        "isBookmarked": 0,
        "questionId": 1 
      },
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 2    
      },
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 3    
      }
    ]   
  }

Can someone help with it?

CodePudding user response:

Your attempt didn't work because o2's questions array completely replaces o1's.

Instead, if there were no duplicates you could use spread to copy the other object properties and to append o2's questions to the ones from o1:

const o3 = {
    ...o1,
    ...o2,
    questions: [...o1.questions, ...o2.questions]
};

Or if you wanted to avoid spread syntax for some reason, you could use Object.assign and Array.prototype.concat:

const o4 = Object.assign({}, o1, o2, {
    questions: o1.questions.concat(o2.questions)
});

Live Example:

const o1 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 2,
            "isBookmarked": 0,
            "questionId": 1
        }
    ]
};

const o2 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 2
        },
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 3
        }
    ]
};

const o3 = {
    ...o1,
    ...o2,
    questions: [...o1.questions, ...o2.questions]
};
console.log(o3);

// Or with `Object.assign`:

const o4 = Object.assign({}, o1, o2, {
    questions: o1.questions.concat(o2.questions)
});
console.log(o4);
.as-console-wrapper {
    max-height: 100% !important;
}

Note, though, that Object.assign (even the one in your question) is multiple loops. They just aren't in your code. :-)

But you've said there may be duplicates (I'm assuming based on questionId) and that the ones from o1 should win over the ones from o2. That's slightly more complicated, but not that much:

const o3 = {
    ...o1,
    ...o2,
    questions: [...new Map([
        ...o2.questions.map(q => [q.questionId, q]),    // Note o2 first...
        ...o1.questions.map(q => [q.questionId, q]),    // ...so duplicates overwrite
    ]).values()]
};

Instead of four loops (two copying the objects, two copying the arrays), we now have eight I think (two copying the objects, two mapping the arrays, two copying the arrays, one building the Map, and one extracting the Map's values) plus some indexed lookups when building the Map.

Live Example:

const o1 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 2,
            "isBookmarked": 0,
            "questionId": 1
        },
        { // Duplicated below with different status
            "status": 1,
            "isBookmarked": 0,
            "questionId": 4
        }

    ]
};

const o2 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 2
        },
        { // Duplicated above with different status
            "status": 2,
            "isBookmarked": 0,
            "questionId": 4
        },
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 3
        }
    ]
};

const o3 = {
    ...o1,
    ...o2,
    questions: [...new Map([
        ...o2.questions.map(q => [q.questionId, q]),    // Note o2 first...
        ...o1.questions.map(q => [q.questionId, q]),    // ...so duplicates overwrite
    ]).values()]
};
console.log(o3);
.as-console-wrapper {
    max-height: 100% !important;
}

If that's too many, we can keep it to four (plus some indexed lookups in the Set):

const o3 = {
    ...o1,
    ...o2,
    questions: []
};
const seen = new Set();
for (const q of o1.questions) {
    o3.questions.push(q);
    seen.add(q.questionId);
}
for (const q of o2.questions) {
    if (!seen.has(q.questionId)) {
        o3.questions.push(q);
    }
}

Live Example:

const o1 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 2,
            "isBookmarked": 0,
            "questionId": 1
        },
        { // Duplicated below with different status
            "status": 1,
            "isBookmarked": 0,
            "questionId": 4
        }

    ]
};

const o2 = {
    "customerId": "ABC",
    "questions": [
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 2
        },
        { // Duplicated above with different status
            "status": 2,
            "isBookmarked": 0,
            "questionId": 4
        },
        {
            "status": 1,
            "isBookmarked": 1,
            "questionId": 3
        }
    ]
};

const o3 = {
    ...o1,
    ...o2,
    questions: []
};
const seen = new Set();
for (const q of o1.questions) {
    o3.questions.push(q);
    seen.add(q.questionId);
}
for (const q of o2.questions) {
    if (!seen.has(q.questionId)) {
        o3.questions.push(q);
    }
}
console.log(o3);
.as-console-wrapper {
    max-height: 100% !important;
}

CodePudding user response:

You can use reduce method to solve your problem

var o1 = {
    "customerId": "ABC",        
    "questions": [
        {
        "status": 2,
        "isBookmarked": 0,
        "questionId": 1 
        }
      ]      
    }
   

 var o2 = {
  "customerId": "ABC",    
  "questions": [
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 2    
      },
      {
      "status": 1,
      "isBookmarked": 1,
      "questionId": 3    
      }
    ]   
  }
  
  const o3 =  o2.questions.reduce((arr, item) => {
    arr.questions.push(item);
    return arr;    
}, o1);
console.log(o3);

CodePudding user response:

I think it's working as intended, the issue is you're trying to merge object with the same exact properties, therefore the properties inside the second object are overriding the properties in the first.

Try adding other, different properties to both objects and you'll see the method is actually working.

  • Related