0

I've got an array of strings. A few of the strings in this array contain a certain substring I'm looking for. I want to get an array of those strings containing the substring.

I would hope to do it like this:

a = ["abc", "def", "ghi"]
o.select(&:include?("c"))

But that gives me this error:

(repl):2: syntax error, unexpected ')', expecting end-of-input
o.select(&:include?("c"))
                        ^
Jones
  • 1,154
  • 1
  • 10
  • 35
  • You need to use a block `o.select{|e| e.include?('c')}` – Sebastián Palma Jul 13 '17 at 18:08
  • 6
    `['abc', 'def', 'ghi'].grep(/c/)` would work as well. – Stefan Jul 13 '17 at 18:28
  • 1
    You are trying to use what is called "pretzel colon" syntax, but like @SebastiánPalma said, it doesn't accept arguments. Check these two docs for more about pretzel colon syntax: http://technology.customink.com/blog/2015/06/08/ruby-pretzels/ and https://stackoverflow.com/questions/1961030/ruby-ampersand-colon-shortcut – anothermh Jul 13 '17 at 20:06
  • Thanks, I didn't know it was called "pretzel colon syntax", I spent way too long trying to google "ruby ampersand meaning" and variants thereof. – Jones Jul 14 '17 at 17:37

3 Answers3

5

You can use the &-shorthand here. It's rather irrational (don't do this), but possible.

If you do manage to find an object and a method so you can make checks in your select like so:

o.select { |e| some_object.some_method(e) }

(the important part is that some_object and some_method need to be the same in all iterations)

...then you can use Object#method to get a block like that. It returns something that implements to_proc (a requirement for &-shorthand) and that proc, when called, calls some_method on some_object, forwarding its arguments to it. Kinda like:

o.m(a, b, c) # <=> o.method(:m).to_proc.call(a, b, c)

Here's how you use this with the &-shorthand:

collection.select(&some_object.method(:some_method))

In this particular case, /c/ and its method =~ do the job:

["abc", "def", "ghi"].select(&/c/.method(:=~))

Kinda verbose, readability is relatively bad.
Once again, don't do this here. But the trick can be helpful in other situations, particularly where the proc is passed in from the outside.


Note: you may have heard of this shorthand syntax in a pre-release of Ruby 2.7, which was, unfortunately, reverted and didn't make it to 2.7:

["abc", "def", "ghi"].select(&/c/.:=~)
D-side
  • 9,150
  • 3
  • 28
  • 44
  • Pretty nifty! I've not seen that before. – Cary Swoveland Jul 13 '17 at 19:38
  • @Stefan nah, its use tends to be longer than the usual definition because of the lengthy `.method`. So I can't come up with any use case for it in golf. Except... when competing for the least number of tokens maybe. – D-side Jul 14 '17 at 08:59
  • Thanks for the lesson on pretzel colon syntax. I ended up using: `o.select do |str|; str.include?("c"); end` (except with newlines instead of semicolons). – Jones Jul 14 '17 at 17:36
  • @Jones "pretzel colon" refers to `&` (pretzel) with a **symbol** (colon+word). This answer was supposed to show that you can use other values (than symbols) too ("pretzel damn-huge-method-chain", LOL). It's quite rare though. And it makes sense to tick the answer you used in the end. I don't mind those 15 rep, and having a tick on an answer that almost starts with "don't do this" feels odd. – D-side Jul 14 '17 at 18:36
5

If your array was a file lines.txt

abc
def
ghi

Then you would select the lines containing c with the grep command-line utility:

$ grep c lines.txt
abc

Ruby has adopted this as Enumerable#grep. You can pass a regular expression as the pattern and it returns the strings matching this pattern:

['abc', 'def', 'ghi'].grep(/c/)
#=> ["abc"]

More specifically, the result array contains all elements for which pattern === element is true:

/c/ === 'abc' #=> true
/c/ === 'def' #=> false
/c/ === 'ghi' #=> false
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • 2
    Another example (though not applicable here) is `["a", 1, {b: 2}, [3,4], {c: 5}].grep(Hash) #=> [{b: 2}, {c 5}]`, because `Hash === {b: 2}` is equivalent to `{b: 2}.instance_of?(Hash) #=> true`. – Cary Swoveland Jul 13 '17 at 21:56
2

You are almost there, you cannot pass parameter in &:. You can do something like:

o.select{ |e| e.include? 'c' }
Radix
  • 2,527
  • 1
  • 19
  • 43