1

I am trying to understand the array "includes" function. My goal is to determine if an array includes a certain item. It works fine for an array of strings but when using objects it doesn't work.

    var itemsString = ["name1", "name2"];
    var itemsObject = [{ name: "name1" }, { name: "name2" }];
    var itemToSearch = { name: "name1" };
    
    console.log(itemsString.includes("name1" ));
    console.log(itemsObject.includes(itemToSearch));
    console.log(itemsObject.includes(x => x.name === "name1"));

Output:

  • true
  • false
  • false

Does "includes" work with objects or do I need to use another function?

ADyson
  • 57,178
  • 14
  • 51
  • 63
Bonomi
  • 2,541
  • 5
  • 39
  • 51
  • 1
    `[{ name: "name1" }, { name: "name2" }];` item at index 0 is actually different from `var itemToSearch = { name: "name1" };`, since they are **different objects**. Despite both objects have the same properties they are **different instances**, hence they have a **different reference**, hence they're not the same. If you **stringify them**, however, they will be the same, in that case. – briosheje Mar 29 '19 at 10:08
  • 2
    Just to dispel confusion `Array#includes` works with objects the same way that all object comparison works - it would find the entry only if it's *literally the same object instance*, not if it's an object that looks similar. – VLAZ Mar 29 '19 at 10:08
  • `itemsObject.some(x => x.name === "name1")` – Robby Cornelissen Mar 29 '19 at 10:08
  • @giorgim thanks, reconsidered – baao Mar 29 '19 at 10:09
  • 1
    Your last attempt goes in the right direction, only the method to use is not `.include()` but [`.some()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some). – axiac Mar 29 '19 at 10:09
  • For `itemsObject.includes(itemToSearch)`: [`Array.prototype.includes` - Step 7b](https://www.ecma-international.org/ecma-262/7.0/#sec-array.prototype.includes) -> [`SameValueZero` - Step 3](https://www.ecma-international.org/ecma-262/7.0/#sec-samevaluezero) -> [`SameValueNonZero` - Step 8](https://www.ecma-international.org/ecma-262/7.0/#sec-samevaluenonnumber) – Andreas Mar 29 '19 at 10:13

3 Answers3

4

You need to use Array.prototype.some() in this case. Array.prototype.includes() does not accept a function parameter*, and you're not testing for strict equality.

const itemsObject = [{ name: "name1" }, { name: "name2" }];

console.log(itemsObject.some(x => x.name === "name1"));

*Peer pressure from the comments section forces me to clarify that includes() does accept function parameters, but will not use the passed function as a predicate to determine whether a given item matches. Rather, it will try to find an item in the array that is strictly equal to the passed function.

Robby Cornelissen
  • 91,784
  • 22
  • 134
  • 156
  • 2
    "*does not accept a function parameter*" it does accept a function parameter. If it didn't, it would throw an error. What happens is that function isn't executed but checked against the array. No object of that array is the function itself, therefore, it returns `false`. – VLAZ Mar 29 '19 at 10:12
  • @VLAZ OK, my phrasing was somewhat incorrect. I meant: does not accept a function parameter that will be used as a predicate to determine whether a given array item matches. – Robby Cornelissen Mar 29 '19 at 10:21
  • Then please adjust your answer accordingly. – Andreas Mar 29 '19 at 10:34
3

In your last line you check wether a function is inside of your array. .includes also works for objects, but it compares them by reference. In your case you probably want to .find or check wether .some of the objects match your query.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
1

Does "includes" work with objects or do I need to use another function?

Includes works with objects, but it compares objects by reference. In your case, despite the first element of itemsObject has the same keys and values of itemToSearch, they are different objects, hence includes will not work, since, for objects cases, it looks for the same object instance.

In order to make it work, you can use several alternatives, like .find, .some, .filter.

Another solution, which I personally don't recommend but I think that it's worth mentioning, is that you can use .includes if you first map items to strings instead. In that case, using JSON.stringify, you can check whether the objects are the same. BEWARE: this will work with single key items only. JSON.stringify doesn't preserve key and values order, so it works with single keys objects only (unless keys and values are in the same order in the original stringified object). Moreover, the JSON.stringify way is way heavier and less performant than the others, I just think it's worth mentioning that as an example.

Below some examples with each of them.

var itemsString = ["name1", "name2"];
    var itemsObject = [{ name: "name1" }, { name: "name2" }];
    var itemToSearch = { name: "name1" };
    
    console.log(itemsObject.some(r => r.name === itemToSearch.name));
    console.log(!!itemsObject.find(r => r.name === itemToSearch.name));
    //          ^--- !! is used to cast to boolean.
    console.log(itemsObject.filter(r => r.name === itemToSearch.name).length > 0);
    console.log(itemsObject.map(i => JSON.stringify(i)).includes(JSON.stringify(itemToSearch)));
    //          ^--------------^                       ^---------------------------------------^
    //            |------ this will stringify each object, converting it to a json string.  |
    //                                                                                      |
    //                this will check whether the string[] includes any stringified value.--^
briosheje
  • 7,356
  • 2
  • 32
  • 54