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
?
- 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
3 Answers
Reset to default 5As 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