简体   繁体   中英

Find out difference and intersection in JavaScript array of objects

Let me first explain the issue I am trying to solve.

I have an existing list of Tags(key-value pair), which will be loaded first when a UI dialog will open up. Then user can add more tags, can remove tags or can edit the tags. The API where I will pass the modified tag list, unfortunately, does not support edit. So edit means: delete the modified entry and add the new entry.

An example of the tags are:


const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [{"key": "t1", "value": "v1"}, {"key": "t2", "value": "v2"}, {"key": "t3", "value": "v3"},];

The code I have tried out is this: (It somehow works, but not elegant). Looking for some element solution, if possible.

<!DOCTYPE html>
<html>
<body>

<h1>JavaScript Arrays</h1>
<h2>The concat() Method</h2>


<p id="demo">TagsToBeAdded</p>
<p id="demo1">TagsToBeRemoved</p>
<p id="demo2">FinalAddList</p>


<script>
const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [{"key": "t1", "value": "v1"}, {"key": "t2", "value": "v2"}, {"key": "t3", "value": "v3"},];
const tagsToBeAdded = modifiedTags.filter(item1 => !existingTags.some(item2 => (item2.key === item1.key && item2.value === item1.value)));
let tagsToBeDeleted = existingTags.filter(item1 => modifiedTags.some(item2 => (item2.key === item1.key && item2.value !== item1.value)));
const tagsToBeRetained = existingTags.filter(item1 => !tagsToBeDeleted.some(item2 => (item2.key === item1.key && item2.value === item1.value)));
if (!tagsToBeAdded.length && !tagsToBeDeleted.length) {
                tagsToBeDeleted = existingTags.filter(item1 => !modifiedTags.some(item2 => (item2.key === item1.key)));
}

const finalTagsListToBeAdded= tagsToBeRetained.concat(tagsToBeAdded);

const children = JSON.stringify(tagsToBeAdded); 
const children1 = JSON.stringify(tagsToBeDeleted); 
const children2 = JSON.stringify(finalTagsListToBeAdded); 

document.getElementById("demo").innerHTML = children
document.getElementById("demo1").innerHTML = children1
document.getElementById("demo2").innerHTML = children2
</script>

</body>
</html>

Some of the examples:

// Input
const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [{"key": "t1", "value": "v1"}, {"key": "t2", "value": "v21"}, {"key": "t3", "value": "v3"}];

// Expected output:
tagsToBeAdded: [{"key":"t2","value":"v21"},{"key":"t3","value":"v3"}]
tagsToBeDeleted: [{"key":"t2","value":"v2"}]
finalTagsList: [{"key":"t1","value":"v1"},{"key":"t2","value":"v21"},{"key":"t3","value":"v3"}]
// Input:
const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [{"key": "t1", "value": "v1"}, {"key": "t2", "value": "v2"}, {"key": "t3", "value": "v3"}];

// Expected output:
tagsToBeAdded: [{"key":"t3","value":"v3"}]
tagsToBeDeleted: []
finalTagsList: [{"key":"t1","value":"v1"},{"key":"t2","value":"v2"},{"key":"t3","value":"v3"}]

// Input:
const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [{"key": "t1", "value": "v1"}];  // t2:v2 is removing

// Expected output:
tagsToBeAdded: []
tagsToBeDeleted: [{"key":"t2","value":"v2"}]
finalTagsList: [{"key":"t1","value":"v1"}]
// Input:
const existingTags = [{
                    "key": "t1",
                    "value": "v1"
                }, {
                    "key": "t2",
                    "value": "v2"
                }
            ];
const modifiedTags = [];  // all tags are dropped

// Expected output:
tagsToBeAdded: []
tagsToBeDeleted: [{"key":"t1","value":"v1"},{"key":"t2","value":"v2"}]
finalTagsList: []

Lodash is also ok.

Create shallow copies of the original arrays. Search through the copied arrays for elements that are in both and move them to their own array.

This causes the copy of existingTags to be reduced to tagsToBeDeleted , the copy of modifiedTags to be reduced to tagsToBeAdded , and the moved duplicates end up in the unchanged tags.

The unchanged tag array is then concatenated with the tagsToBeAdded array to make finalTagsList .

Note that modifiedTags and finalTagsList are effectively the same thing. Because of this, the below could be optimized to just drop the duplicate elements from the array copies.

const mangle = (existingTags, modifiedTags) => {
  const unchangedTags = [];
  existingTags = Array.from(existingTags);
  modifiedTags = Array.from(modifiedTags);
  for(let i = 0; i < existingTags.length; i++) {
    const a = existingTags[i];
    for(let j = 0; j < modifiedTags.length; j++) {
      const b = modifiedTags[j];
      if (a.key == b.key && a.value == b.value) {
        unchangedTags.push(a);
        existingTags.splice(i, 1);
        modifiedTags.splice(j, 1);
        i--;
        break;
      }
    }
  }
  return {
    tagsToBeAdded: modifiedTags,
    tagsToBeDeleted: existingTags,
    finalTagsList: unchangedTags.concat(modifiedTags);
  };
};

A straight forward (not necessarily the most efficient, but for a set of tags that's manually managable, I don't think it makes difference) approach would be the following. This assumes, that the key of a tag is unique and both, key and value are primitive types that can be compared with === . See the comments in code for explanation.

 const existingTags = [ { "key": "t1", "value": "v1"}, { "key": "t2", "value": "v2" }, { "key": "t3", "value": "v3" } ]; const modifiedTags = [ {"key": "t1", "value": "v1u"}, {"key": "t3", "value": "v3"}, {"key": "t4", "value": "v4"} ]; //tags that exist in existingTags but not in modifiedTags have to be deleted, const delTags1 = existingTags.filter(x => !modifiedTags.find(y => x.key === y.key)); //tags that exist in both but differ in value have to be deleted const delTags2 = existingTags.filter(x => !!modifiedTags.find(y => x.key === y.key && x.value !== y.value)); //tags that exist in modifiedTags but not in existingTags have to be added const addTags1 = modifiedTags.filter(y => !existingTags.find(x => x.key === y.key)); //tags that exist in both but have a different value have to be readded const addTags2 = modifiedTags.filter(y => existingTags.find(x => x.key === y.key && x.value !== y.value)); //tags that exist in both and have the same value have to be retained const keepTags = existingTags.filter(x => modifiedTags.find(y => x.key === y.key && x.value === y.value)); const finalDelete = [...delTags1, ...delTags2]; const finalAdd = [...addTags1, ...addTags2, ...keepTags]; console.log(JSON.stringify(finalDelete)); console.log(JSON.stringify(finalAdd));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM