So far as I can tell, array.reject
and array.select
do nothing:
[nil, false, true].reject # Should have been reject.to_a for this example.
=> [nil, false, true]
[nil, false, true].select # Should have been select.to_a for this example.
=> [nil, false, true]
For the code I was trying to write, compact
was what I needed, but I'm very curious why reject
and select
without a block do nothing - I was expecting a default block of { |e| e }
so reject
would be compact
and 'select' would be some weird anti-compact.
What is the default block doing?
Edit: Sorry, I missed off the '.to_a' on the ends of the expressions above, which I was hoping would trigger some sort of lazy evaluation and make the reject/select enumeration do something useful. I normally cut&paste my examples to avoid this sort of thing.
A block is optional for many Ruby methods. When no block is given an enumerator is usually returned. There are at least a couple of reasons you might want an enumerator.
#1 Use the enumerator with the methods in the class Enumerator.
Here's an example. Suppose you wish to alternate the case of letters in a string. One conventional way is:
"oh happy day".each_char.with_index.map { |c,i| i.odd? ? c.upcase : c.downcase }.join
#=> "oH HaPpY DaY"
but you could instead write:
enum = [:odd, :even].cycle
"oh happy day".each_char.map { |c| enum.next==:odd ? c.upcase : c.downcase }.join
or perhaps
enum = [:upcase, :downcase].cycle
"oh happy day".each_char.map { |c| c.send(enum.next) }.join
See the docs for Array#cycle and Enumerator#next.
#2 Use enumerators to chain methods
In my first example above, I wrote:
"oh happy day".each_char.with_index.map...
If you examine the docs for String#each_char and Enumerator#with_index you will see that both methods can be used with or without a block. Here they are both used without a block. That enables the three methods to be chained.
Study the return values in the following.
enum0 = "oh happy day".each_char
#=> #<Enumerator: "oh happy day":each_char>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: "oh happy day":each_char>:with_index>
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: #<Enumerator:
# "oh happy day":each_char>:with_index>:map>
You might want to think of enum1
and enum2
as "compound" enumerators.
You show the return value of:
[nil, false, true].reject
to be:
#=> [nil, false, true]
but that is not correct. The return value is:
#<Enumerator: [nil, false, true]:reject>
If we write:
enum = [nil, false, true].reject
then:
enum.each { |e| e }
#=> [nil, false]
(which, since Ruby v2.3, we could write enum.reject(&:itself)
). This uses the method Enumerator#each, causing enum
to invoke Array#each because reject
's receiver is an instance of the class Array
.