最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

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?

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?

Share Improve this question asked Oct 30, 2019 at 1:10 hectoraloneohectoraloneo 971 silver badge6 bronze badges 6
  • You can use find() to check if an object exists in an array before pushing it. – Nikhil Commented Oct 30, 2019 at 1:13
  • As @Nikhil mentioned you can use find or filter to check if that element already exits before pushing Or if you want to do it in one go refer to this link stackoverflow./questions/38613654/… – Wamiq Rehman Commented Oct 30, 2019 at 1:19
  • checking for object equality is tricky in javascript, the ments for find() or filter() make it sound like it's a simple built in array method that will do the trick, but you either have to pare all of the keys and values manually, or do a thing like JSON.stringify to serialize the objects and pare those strings.... – Dan Oswalt Commented Oct 30, 2019 at 1:24
  • do you have to pare all the fields, or just a name? or, could you also attach a unique id to each? – Dan Oswalt Commented Oct 30, 2019 at 1:26
  • 3 @DanOswalt It's not tricky at all, you just have to know what specifically needs to be checked. JSON.stringify is insufficient as object properties may be created in any order. There are many libraries that do this in a variety of ways. – Dave Newton Commented Oct 30, 2019 at 1:41
 |  Show 1 more ment

3 Answers 3

Reset to default 5

As the various menters 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 bee 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 paring objects with a name, position and grade property, you could write a custom parison 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 parison of your objects (such as lodash isEqual), or looking on Stack Overflow for how people have approached spinning there own solution for deep parisons:

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./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 remend 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 ments 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

发布评论

评论列表(0)

  1. 暂无评论