1

I have an array with objects inside. I get the array from my firestore database. When I get the array, I check if my user has any of the objects. When I check this using includes() I get false as a return, but the array clearly has the object. What can be the issue?

Check:

            const querySnapshot = await getDocs(colRef);

            querySnapshot.forEach(async(badge) => {
                console.log(userData.obtainedBadges)
                if(!userData.obtainedBadges.includes(badge.data())){                        
                    console.log(badge.data())
                    console.log("not includes")
                    setObtainableBadges(prev => {
                        return [...prev, badge.data()]
                     })
                } else {
                    console.log("includes")
                    setUserBadges(prev => {
                        return [...prev, badge.data()]
                     })
                }
            })

For example one of the objects I check( badge.data() ) :

Object { id: 1, desc: "Play 5 games.", type: "games", reward: 10, condition: 5, img: "/trophies/firsttrophy.png", name: "The First Games" }

querySnapshot Array:

Array(9) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…} ]
​
0: Object { img: "/trophies/firsttrophy.png", type: "games", name: "The First Games", … }
​
1: Object { id: 2, type: "games", name: "The Beginner", … }
​
2: Object { name: "The Big Brain", id: 50, type: "games", … }
​
3: Object { condition: 50, id: 4, desc: "Play 50 games.", … }
​
4: Object { name: "Noob", reward: 10, img: "/trophies/noobmedal.png", … }
​
5: Object { id: 6, reward: 10, desc: "Reach a minimum high score of 2000.", … }
​
6: Object { condition: 3000, desc: "Reach a high score of 3000.", name: "Unstoppable", … }
​
7: Object { desc: "Reach a score of 0 in any gamemode.", id: 9, reward: 25, … }
​
8: Object { type: "easteregg", condition: "easteregg", desc: "Find an easter egg!", … }
​
length: 9
​
<prototype>: Array []

You can see, that my object is the first element in the array. But for some reason, includes() returns false.

5
  • 5
    Objects are compared by identity, not content. Two objects are only.equal if they are actually the exact same object. Commented Oct 6, 2022 at 20:24
  • Oh okay, how can I achieve what I want then? Commented Oct 6, 2022 at 20:27
  • 2
    See stackoverflow.com/questions/201183/… Commented Oct 6, 2022 at 20:27
  • And use array.some() to search for equal objects with one of those solutions. Commented Oct 6, 2022 at 20:28
  • 2
    In a nutshell, obtainedBadges.find(ob => ob.id === badge.data().id) if that's truthy Commented Oct 6, 2022 at 20:28

3 Answers 3

2

When you compare two objects in JavaScript it is not enough that their keys and values match. All objects have internal ids and therefor two objects or arrays with the same keys and values will in a regular comparison like == still return false. See example :

const object1 = {test:'test'}
const object2 = {test:'test'}
console.log(object1 == object2)
The same goes for arrays, maps, sets and all non-primitive values in JavaScript. It is however possible to compare objects, arrays non-primitives but only by comparing all their properties. This can get really complicated, as objects can contain other non-primitives and to make a general use function you would need to make a recursive function that can handle any JavaScript type, which would be total overkill for you situation. In a case like this one you should check on a unique value, id for example, instead of comparing the whole objects. Something like this :

const badge_in_user_data = userData.obtainedBadges.some(badge => badge.id == badge.data().id)

Sign up to request clarification or add additional context in comments.

1 Comment

"In JavaScript I recommend not comparing objects" - LOL
0

includes searches a value inside an object. Yet, similar objects are not the same, just like twins are different persons. Proof:

console.log({} === {});

So, since includes does not search for similar objects, but for the specific object, your

if (!userData.obtainedBadges.includes(badge.data())) {

is incorrect. Instead, you will need to .filter() yourself, like:

if (userData.obtainedBadges.filter(item => item.id === badge.data().id).length === 0) {

Which searches a match for badge.data() inside userData.obtainedBadges by id, retrieves an array, whose size is 0 if no such match was found.

Comments

-1

I just fixed it by my own function instead of .includes():

function areIdentical(actualValue, expectedValue) {
    let actualValueKeys = Object.keys(actualValue);
    let expectedValueKeys = Object.keys(expectedValue);

    if (actualValueKeys.length != expectedValueKeys.length) {
        return 0;
    } else {
        let result = 0;
        actualValueKeys.forEach(x => {
            if (actualValue[x] == expectedValue[x]) result += (1 / actualValueKeys.length) * 100;
        });

        return result;
    }
}

and You can use: (yourArray.filter(x => areIdentical(x, value) > 99).length > 0) ? true : false

3 Comments

Why does areIdentical return a number, not a boolean?
This number allows you to control the percentage of coincidence between objects.
I assumed so, but that's both confusing and not what the OP asked for

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.