1

Inside the Chrome Console I have a string:

var string = 'john, john, bob, nina, colin, bob, luck, robert, nina, john, jack';

Then it is being modified and assigned to a new value using two, seeming identical to me, methods:

First method:

var string2 = string.split(", ").filter(function(value, index, array){
    return index === array.indexOf(value);
}).join(', ');

Second method:

var string3 = string.split(", ").filter(function(value, index, array){
    if(index === array.indexOf(value)) return index;
}).join(', ');

What should both methods do:

  1. Turn the string into an array of each word separately.
  2. Filter the array so that no duplicate values exist.
  3. Turn the array back into a string format.

What I would expect values of string2 and string3 to be :

"john, bob, tony, nina, colin, luck, robert, jack"

They should be strictly identical (based on my logic).

What I actually get:

string2 = "john, bob, tony, nina, colin, luck, robert, jack";
string3 = "bob, tony, nina, colin, luck, robert, jack"

"John" is missing in the second string.

What I already checked:

  1. The strict comparison (===), that is being performed inside the if-statement () is true for the first "john".
  2. Both index (parameter of the function) and index of the value of the first "john" are 0 as expected.

What I ask:

Explain, please, why do the results differ?

Eduard
  • 8,437
  • 10
  • 42
  • 64
  • The first makes no sense. You have to always return a boolean value from the `filter` predicate function - not sometimes `undefined` and sometimes a number. – Bergi Aug 12 '17 at 13:52
  • Your "what I actually get" results look like they're reversed. – spanky Aug 12 '17 at 13:53
  • 1. I got the function, that is being used for string2 from here: https://stackoverflow.com/questions/16843991/remove-occurrences-of-duplicate-words-in-a-string 2. Swapped the functions in the post due to the mistake. Now it is correct. – Eduard Aug 12 '17 at 13:54
  • Your second method works fine for me, returning no duplicates: `string3 == "john, bob, nina, colin, luck, robert, jack"`. I'm not sure where tony came from in your expected result. – Bergi Aug 12 '17 at 13:55

3 Answers3

2

Your second example:

var string2 = string.split(", ").filter(function(value, index, array){
    if(index === array.indexOf(value)) return index;
}).join(', ');

The line if(index === array.indexOf(value)) return index; is wrong.

That will return index if index === array.indexOf(value), and nothing if not. You need to return true or false.

In JavaScript, all numbers except 0 convert to true, and 0 converts to false. So if the index is 0, you are basically returning false instead of true.

This is the correct code:

var string2 = string.split(", ").filter(function(value, index, array){
    if(index === array.indexOf(value)) return true;
    else return false;
}).join(', ');
Oliver Ni
  • 2,606
  • 7
  • 29
  • 44
  • the "correct code" is superfluous, in case of just retuning the comparison result. – Nina Scholz Aug 12 '17 at 14:03
  • @NinaScholz He asked what the difference was. The second one was actually wrong, so by correcting it to what he wanted it to be, they are now basically identical. – Oliver Ni Aug 12 '17 at 14:05
  • The "correct code" lacks a `return false` in the `else` case. Relying on `undefined` being a falsy value is just the same bad style the OP fell to. – Bergi Aug 12 '17 at 14:07
  • it's still a messy code, with `if (...) return true; else return false;`. – Nina Scholz Aug 12 '17 at 14:09
  • @NinaScholz Obviously. I was correcting his second example so it would still look like his second example. The first example would still be better. – Oliver Ni Aug 12 '17 at 14:10
1
if (index === array.indexOf(value)) return index; // returns 0, at index 0,
                                                  // should return true

vs

return index === array.indexOf(value);

It's just the problem in the first case on index zero, you return the index, which is zero and this resolves for Array#filter to false, whereas it should be true, like in the second case.

Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    That's the issue! So, I just need to return **value** instead of **index**? Thank you, @Nina Scholz! – Eduard Aug 12 '17 at 13:57
  • @Eduard No, you should return neither the value nor the index. You should return a boolean. – Bergi Aug 12 '17 at 13:58
  • @bergi it works if I return the **value** (although, I understand that it is semantically incorrect). So, there is no **correct** way out of this, if I keep using **.fiter()**. Should I then swap the **.filter()** function for something else? – Eduard Aug 12 '17 at 14:01
  • 1
    @Eduard You return `true`, check my answer. – Oliver Ni Aug 12 '17 at 14:02
  • filter expect a boolean value for deciding if an element should go to the result set or not. the comparison with index and indexOf returns a boolean value, which can be used here. – Nina Scholz Aug 12 '17 at 14:02
  • 1
    @Eduard returning the `value` might work in your specific case, but it's not correct. I repeat: `filter` expects the callback to return a boolean (true or false). Anything else will be cast to one. And falsy values (like the `index` 0, or a empty string `value`) will mess up the result. So just use `return check`, or `if (check) return true; else return false;` if you insist on an `if` statement. – Bergi Aug 12 '17 at 14:05
  • @Bergi Got you! Thanks! – Eduard Aug 12 '17 at 15:34
1

Your results are reversed. The first code block is the one that eliminates "John". (Question was edited to correct this)

For string3, it eliminates "John" because the only time it will return an index for "John" is when "John" is at index 0, and that's a falsey value, so "John" will not be included in the result.

It's clearer if you return a boolean value, like in the working example, so you don't need to deal with issues when the value itself is falsey.

spanky
  • 2,768
  • 8
  • 9