Accepting an array as the first parameter to String.prototype.includes

# Edwin Reynoso (10 years ago)

There are times where I would like to check whether a string has every occurrence of certain strings/numbers:

var str = "John, Mary, Bob, Steve";

str.includes(["Mary", "Bob"]); // true

var str2 = "John, Mary, Steve";

str2.includes(["Mary", "Bob"]); // false

So the way the above would work would be as an AND operation. Meaning having all of the instances of the array past in.

To use String.prototype.includes as an OR operation with the first parameter being an array, wouldn't be necessary because you can use a regular expression for that:


var str = "John, Mary, Bob, Steve";

(/Mary|Bob/g).test(str); // true

 var str2 = "John, Mary, Steve";

(/Mary|Bob/g).test(str2); // true

As of right now String.prototype.includes called with an array as the first parameter will call .toString() on the array resulting in the following:

var str = "Hello World";

str.includes(["Hello", "World"]); //false because it calls ["Hello",
"World"].toString() == "Hello,World";


Pretty useless!!!

Now to achieve what I would like String.prototype.includes to accomplish with an array as the first parameter, I currently take the following approach:


var str = "John,Mary,Bob,Steve";

var names = ["Mary", "Bob"];

names.every(name => str.includes(name)); // true;


//Or perhaps make it a function:

function stringIncludes(str, names) {
  if(names) {
     return names.every(name => str.includes(name));
  }
  return false;
}

Thanks.
# Bergi (10 years ago)

Edwin Reynoso wrote:

There are times where I would like to check whether a string has every occurrence of certain strings/numbers:

Now to achieve what I would like String.prototype.includes to

accomplish

with an array as the first parameter, I currently take the following approach:

var str = "John,Mary,Bob,Steve"; var names = ["Mary", "Bob"]; names.every(name => str.includes(name)); // true;

And that's perfectly fine imho, pretty expressive about what is done about the array. Just passing an array to .includes is rather meaningless (not denotative).

If we need a method to do this (to allow for native optimisations with fancy string search algorithms), I'd suggest to use a different method name like String.prototype.includesAll.

Bergi

# Andrea Giammarchi (10 years ago)

I'm still having hard time understanding what's the difference between contains and the good old indexOf beside the RegExp check ... but I agree having multiple explicit searches instead of multiple implicit searches won't make such big difference. Good news is, you probably will still use RegExp anyway because names can be composed and haveing a \bName\b helper is probably what you might need anyway :-)

i.e. Maria !== Marianne

# Garrett Smith (10 years ago)

On 3/10/15, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

I'm still having hard time understanding what's the difference between contains and the good old indexOf beside the RegExp check ... but I agree having multiple explicit searches instead of multiple implicit searches won't make such big difference. Good news is, you probably will still use RegExp anyway because names can be composed and haveing a \bName\b helper is probably what you might need anyway :-)

i.e. Maria !== Marianne

What if Array had a contains or containsAll functions?

var s = "Maria, Mariana"; var a = s.split(/\s*,\s*/); ["Maria", "Mariana"].every(e=>{return a.indexOf(e)!=-1}); true

But a contains function might be better than indexOf

["Maria", "Mariana"].every(e=>{return a.contains(e)});

But containsAll might be even better.

a.containsAll(["Maria", "Mariana"]) seems even easier to read for me.

There is already filter for exclusions. What about merging arrays?

Array.union(array2, array3, ...);

# Jordan Harband (10 years ago)

Would new Set(arr.concat(substrings)).length === arr.length work? Personally I find the "some" approach the clearest, though.

# Andrea Giammarchi (10 years ago)

contains better than indexOf ? I'd agree only if contains wasn't accepting any extra argument, which makes it even more (pointless?) similar to indexOf.

If it had only one incoming parameter, you could have ["maria", "marianne"].some(str.contains, str) and win over all other examples. but just the !=-1 or >-1 as reason to prefer contains? ... dunno, I think I

have 99 problems in JS related development, >-1 ain't one :-/

# Andrea Giammarchi (10 years ago)

also, just for people not afraid of the tilde

["Maria", "Mariana"].some(e=>~a.indexOf(e));

yeah, you can use it ^_^

# Garrett Smith (10 years ago)

On 3/10/15, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

contains better than indexOf ? I'd agree only if contains wasn't accepting any extra argument, which makes it even more (pointless?) similar to indexOf.

If it had only one incoming parameter, you could have ["maria", "marianne"].some(str.contains, str) and win over all other examples. but just the !=-1 or >-1 as reason to prefer contains? ... dunno, I think I have 99 problems in JS related development, >-1 ain't one :-/

Evidently.

I can only guess that str is a string. Strings don't have a contains method, nor was one proposed the call str.contains(str) inside of a call to some looks like a confused mistake.

# Andrea Giammarchi (10 years ago)

not sure I understand what you say but ["maria", "marianne"].some(str.contains, str) means ["maria", "marianne"].some(function (value) {return this.contains(value)}, str) ... no bind nor arrow function needed, the function str.contains is executed with str as context per each iteration. Is that confusing? 'cause that's how some/every/forEach/map/filter work

# Edwin Reynoso (10 years ago)

Well the current ES6 .includes() was before named .contains(): [String.prototype.includes] ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#String.prototype.contains )

But if Garrett Smith was trying to point out that .contains() would be better naming for the specific purpose of using an Array: str.contains(["Mary", "Bob"]) then ok? maybe.

This is what I originally had in mind:

String.prototype.includesAll = function(arr, index, onlyOne){
  return arr[onlyOne ? "some" : "every"](el => this.includes(el, index),

this);
}

But like Bergi said "denotative" the above with the onlyOne argument would also make this function denotative as well, and if you wanted to use that argument you would have to specify the 0 as the index all the time.

So perhaps two functions

String.prototype.containsAll = function(arr, index) {
   return arr.every(el => return this.includes(el, index), this);
}

String.prototype.contains= function(arr, index) {
  return arr.some(el => return this.includes(el, index), this);
}

Origianlly I had the names includesAll and includesOneOf, but contains and containsAll is more human readable.

Andrea Giammarchi said:

I'm still having hard time understanding what's the difference between

contains and the good old indexOf

Well like Garrett Smith said it is easier to read, and it gets rid of .indexOf() > -1 which I consider a hack.

Why I consider a hack simply because .indexOf() is used to return the index, then adding > -1 or using a tilda ~ to make it a Boolean. Why?

Also mixing that with a some/every function? and passing a callback, yea I'd choose:

str.contains(["Mary", "Bob"]) over:

["Mary", "Bob"].every(el => el.indexOf(el) > -1);

//OR

["Mary", "Bob"].every(el => el.includes(el));

Also using indexOf() if you want to start from a certain index would add more complexity.

# Garrett Smith (10 years ago)

On 3/10/15, Edwin Reynoso <eorroe at gmail.com> wrote:

Hi Edwin -

Well the current ES6 .includes() was before named .contains(): [String.prototype.includes] ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#String.prototype.contains )

But if Garrett Smith was trying to point out that .contains() would be better naming for the specific purpose of using an Array: str.contains(["Mary", "Bob"]) then ok? maybe.

str.contains was Andrea's interpretation of my example. Nevermind that; let me try again...

You first asked about making String.prototype.includes take an array, check every item in the array, and return false if any array item is not present in the string, else return true. From that, you switched it around to array.every, to make sure every item was in the string.

names.every(name => str.includes(name));

So far so good...

That's essentially what Array.prototype.containsAll would do.

Array.prototype.containsAll(anotherArray)

You can split the string to get anotherArray.

names.every(str.split(/,\s*/));

I cannot explain what ES6 @@split does. The spec have gotten even farther away from how web developers understand the language.

The ECMAScript specification has always been complicated by internal specification mechanisms, and it was because of this obfuscation that I was able to have a bit of an edge on others. I was able to understand the difference between the confusingly same-named [[Prototype]] (an object's internal property) and fn.prototype (the property that every user-defined function gets by default), and that there implies that each user-defined function has both a [[Prototype]] (being Function.prototype) and a .prototype property. It's confusing, but I got it.

The new stuff in the spec, where there is not even String.prototype.split, but @@split is hard to understand. The ECMAScript 3 spec was already too hard for most developers. Things like missing RegularExpressionLiteral, the ActivationObject/VariableObject (which one is it?), PrimitiveValue being a number for Date objects, but where addition calls ToPrimitive with hint "string", and that gets passed to DefaultValue. It's hard. We need to work for the tech companies that steal your data and provide useless junk to the user. Call us rockstars or other insulting titles, but at the end of the day, I need to to is memorize those specs if I want to understand what I am doing in order to pay my rent. It can and should suck less.

ECMAScript 5 added complexity. ECMAScript 6's explanations and specification terminology seems too much for me. The spec shouldn't be so hard that programmers can't understand it. A good spec should serve as a reference for developers to go and look up how it works.

I can't say how I'd write it better because I haven't taken the time to grok it all. Nor do I plan to - life is too short.

# Garrett Smith (10 years ago)

On 3/10/15, Garrett Smith <dhtmlkitchen at gmail.com> wrote:

On 3/10/15, Edwin Reynoso <eorroe at gmail.com> wrote:

[...]

specs if I want to understand what I am doing in order to pay my rent.

It can and should suck less.

Let me rephrase that: I don't mean that the specification sucks - sitting down and trying to figure it out; reading the spec, for me, is not enjoyable work. The spec is hard and frustrating.

But I really do appreciate all the hard work and effort by Brendan, Allen, etc - you guys are all a lot smarter than I am. And ECMAScript is making a lot of improvements.

But I still don't like reading the spec.