0

Array.IndexOf() function behaves different for strings and objects.

stringBehaviour();
objectBehaviour();

function stringBehaviour() {
    let s1 = "helmet";
    let s2 = "trousers";    
    let collection = [];  
    collection.push(s1);
    collection.push(s2);
    console.info(collection.indexOf("trousers"));
};

function objectBehaviour() {
    let obj1 = {item: "helmet"};
    let obj2 = {item: "trousers"};
    let collection = [];
    collection.push(obj1);
    collection.push(obj2);
    console.info(collection.indexOf({item: "trousers"}));
};

what can i do to have indexOf() not looking for the same instance but for equality of the objects elements so that the function objectBehaviour() would return 1?

Bobbey
  • 173
  • 1
  • 4
  • 12
  • 1
    it's because in JS, object's equality works by the reference value. In `console.info(collection.indexOf({item: "trousers"}));`, the object `{item: "trousers"}` is a new object with a different reference than `obj2`. Try with `console.info(collection.indexOf(obj2));` and you'll see it's the same behaviour. The problem is the reference – Kaddath Aug 20 '19 at 16:05
  • 1
    in addition to the question which is marked as duplicated by yours, you should decide how to compare your objects (for your custom `indexOf` implementation). For instance, `{item: "trousers"} != {item: "trousers"}` (these are not the same object, just objects with the same fields) but `JSON.stringify({item: "trousers"}) == JSON.stringify({item: "trousers"})`; but this won't work if your objects may contain methods – YakovL Aug 20 '19 at 16:07
  • thanks, i got that. can i somehow define how js will check for equality while searching a collection after a `indexOf()` call, like you could override the equals method in other programming languages? – Bobbey Aug 20 '19 at 16:08
  • hardly so. But you can do `array.map(item => JSON.stringify(item)).indexOf(JSON.stringify(itemToBeFound))` (or you can define and use and `toComparable` helper instead of `JSON.stringify` – YakovL Aug 20 '19 at 16:11
  • One solution is to use [findIndex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) which you call with a comparison function. For simple cases, you can use `JSON.stringify` as in @YakovL comment (it can be trickier with objects automatically timed with date objects -dates will differ- or recursive structure -JSON will error-) – Kaddath Aug 20 '19 at 16:12

1 Answers1

1

You could use findIndex instead and pass a function which compares objects by their properties.

stringBehaviour();
objectBehaviour();

function stringBehaviour() {
    let s1 = "helmet";
    let s2 = "trousers";    
    let collection = [];  
    collection.push(s1);
    collection.push(s2);
    console.info(collection.indexOf("trousers"));
};

function objectBehaviour() {
    let obj1 = {item: "helmet"};
    let obj2 = {item: "trousers"};
    let collection = [];
    collection.push(obj1);
    collection.push(obj2);
    console.info(collection.findIndex(el => isIdentical(el, { item: "trousers"})));
};

function isIdentical(current, given) {
  return Object.keys(given).every((key) => current[key] === given[key]);
}
dziraf
  • 3,607
  • 1
  • 16
  • 22