0

How can I tell if my string contains a substring from an array?

I thought include? was the key, but apparently not ...

arr = ["aa", "bb", "cc"]
str = "mystringcc"
str.include?(*arr)
ArgumentError: wrong number of arguments (given 3, expected 1)

In the above example, I would expect the result to be true since the string has "cc", which is an element of the array.

4 Answers 4

10

You want to use Enumerable#any?:

arr.any? { |substring| str.include?(substring) }

The solution is very efficient, because this method ensures short-circuit evaluation, meaning, that if you have array of 100000000 substrings and you want to check whether your string contains any of these substrings, this method will stop right when its block returns true, meaning, that if the matching substring is first in the array, it will never check other 99999999 elements.

1
  • 1
    I expect this would be more efficient than using a regex, but not because of the short-circuiting, as the regex would short-circuit as well. Indeed, I can't think of a reasonable way of solving the problem that does not short-circuit. Commented May 16, 2017 at 18:59
4

Construct a Regular Expression (without pipes and slashes and dots and other magic stuff):

arr = ["aa", "bb", "cc"]
str = "mystringcc"
reg = Regexp.union(arr)  # That's all!
p str.match?(reg)        #=> true
0

The reason you are getting that error message is because the * operator is telling Ruby to pass all the items of arr to str.include?. For those arr items, that is equivalent to writing str.include?("aa", "bb", "cc").

As said in other answers, checking a condition is verified for any of the elements contained in an array, like in your case (where you don't want to check if all the elements of an array are substrings of a given string), is done using arr.any? { |item| str.include?(item) }.

-1

Given:

arr = ["a", "b", "x"]
str = "DoDox"

EDIT: As the other comments have pointed out, this is actually the fastest way, as it breaks the evaluation when a single true statement is found:

arr.any?{|substr| str.include?(substr)}

The foldl way: I have used this example to illustrate the power of folding expresions. This solution might be somewhat elegant as an illustrative example for foldings, but it is not sufficiently performant.

arr.map{|substr| str.include?(substr)}.inject{|x,y| x || y}

The map-function maps all possible substrings the original string str can have. inject iterates over the returning array and compares its constituents, using a logical or. If any substring in the array 'arr' is encountered, the result will yield true in the result of map. With the or-relation we return a true value, if any value of the result of map was true. This is slower than any?, because every single value in the array gets evaluated even if a true is encountered (wich would always yield a true at the end of inject).

["a", "b", "x"] -------> [false, false, true] ----------> true
                 map()                          inject()
1
  • 2
    You could use inject instead of map and .any? instead of the second inject, but using .any? to replace it all would be simpler and faster (it will stop on the first positive match).
    – Kris
    Commented May 16, 2017 at 19:51