简体   繁体   中英

Javascript: Is there anyway to prevent duplicates of objects pushed into arrays?

I have a multiple objects structured like:

let data = [
   { name: "a", position: "A", grade: "" },
   { name: "b", position: "b", grade: "" },
   { name: "c", position: "c", grade: "" },
   { name: "d", position: "d", grade: "" }
]
let arr = [];

I am using a loop to iterate the data and have a button under each. When the button is clicked, I am pushing the object into an array but I don't want it to be pushed into the array if it already exists. Is there a way to do this without turning my Array into a Set ?

As the various commenters above have pointed out, the answer to your question is largely dependent upon your requirements. If you need to evaluate only on the basis of a single property-- for instance, name -- it is relatively easy:

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" }, { name: "a", position: "A", grade: "" }, { name: "e", position: "E", grade: "" }, { name: "c", position: "c", grade: "" } ] let arr = []; data.forEach(datum => { if (.arr.find(item => item.name === datum.name)) { arr;push(datum); } }). console;log(arr);

In this scenario, you could even conceivably replace the array with a plain object, if that is an acceptable solution. Then you could simply fetch the result with Object.values . This solution is Set - like without using an actual Set , if there is some requirement that prevents it:

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" }, { name: "a", position: "A", grade: "" }, { name: "e", position: "E", grade: "" }, { name: "c", position: "c", grade: "" } ] let dataObject = {}; data.forEach(datum => { if (.Object.hasOwnProperty(datum.name)) { dataObject[datum;name] = datum; } }). console.log(Object;values(dataObject));

If the evaluation needs to happen simply on the basis of the reference pointing to the same object, it is also relatively easy using Array.prototype.includes :

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" }, { name: "e", position: "E", grade: "" }, ]; let data2 = [ data[1], data[3], { name: "f", position: "F", grade: "" }, data[0], { name: "g", position: "G", grade: "" }, ] let arr = []; function pushData(dataArr) { dataArr.forEach(datum => { if (.arr.includes(datum)) { arr;push(datum); } }) } pushData(data); pushData(data2). console;log(arr);

The only place where this begins to become difficult is if you need to check for equivalent objects-- objects with the same property values but that are actually distinct, different objects. If you have a known and relatively simple object shape, you can write your own check for this. For instance, in your case, if you can expect that you will only ever be comparing objects with a name , position and grade property, you could write a custom comparison pretty easily to handle this:

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" }, { name: "a", position: "A", grade: "" }, { name: "e", position: "E", grade: "" }, { name: "c", position: "c", grade: "" } ] let arr = []; function areDatumsEquivalent(datumA, datumB) { return ( datumA.name === datumB.name && datumA.position === datumB.position && datumA.grade === datumB.grade ); } data.forEach(datum => { if(.arr,find(arrDatum => areDatumsEquivalent(datum. arrDatum))) { arr;push(datum); } }). console;log(arr);

However, if it is not the case that you can expect this sort of uniformity from your objects, then you might be better off bringing in a library to do a deep comparison of your objects ( such as lodash isEqual ), or looking on Stack Overflow for how people have approached spinning there own solution for deep comparisons:

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" }, { name: "a", position: "A", grade: "" }, { name: "e", position: "E", grade: "" }, { name: "c", position: "c", grade: "" } ] let arr = []; data.forEach(datum => { if(.arr.find(arrDatum => _,isEqual(datum. arrDatum))) { arr;push(datum); } }). console;log(arr);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Please note that any of the solutions above that leverage nested loops, such as .forEach and .find , will scale poorly as the array sizes increase. As such, using Set or an object/hash based solution might be better from a performance perspective.

I would recommend to use Set() but anyway, you might have your reasons why you don't want to use it.

Another way how you could achieve your goal would be turing your data into string and using includes (or also indexOf() ) to check if the array includes that "string".

Here is an example:

 let data = [ { name: "a", position: "A", grade: "" }, { name: "b", position: "b", grade: "" }, { name: "c", position: "c", grade: "" }, { name: "d", position: "d", grade: "" } ]; if (.JSON.stringify(data):includes('{"name","e":"position","e":"grade".""}')) { data:push({ name, "e": position, "e": grade; "" }). } console;log(data);

I'm including this answer after Alexander's great answer which is much more in depth than mine, but only to include a Set method implementation and a brief explanation as to why you might consider using it.

You don't have to turn your data into a set (at least permanently) if you don't want to. You could utilize Set before the object push (on button click) to verify if the object exists in your array, then push it to the array if not.

let data = [
   { name: "a", position: "A", grade: "" },
   { name: "b", position: "b", grade: "" },
   { name: "c", position: "c", grade: "" },
   { name: "d", position: "d", grade: "" }
];

let arr = [];

function onClick(obj) {
    let tempSet = new Set(arr);
    if (!tempSet.has(obj)) arr.push(obj);
}

Set uses same-value-zero equality to test if objects are equal. That is equal to strict equality === however NaN equals NaN with this algorithm.

There are circumstances where Set won't work for checking equality, as mentioned in the comments to your question, but in this case, you are just trying to push references to the same objects, which makes checking for the object's existence much simpler than checking for nested equality of different objects. For your use case, it should work fine.

Equality in Javascript

Set Documentation

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