2

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.

3
  • Please give a complete example with input and expected output ... Commented Jun 21, 2022 at 16:06
  • Yes. Added now. Commented Jun 21, 2022 at 17:43
  • There is an npm package called deep-diff that does exactly what you need. Check it out: npmjs.com/package/deep-diff Commented Jun 21, 2022 at 17:59

2 Answers 2

3

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);
  };
};
Sign up to request clarification or add additional context in comments.

Comments

1

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));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.