@paulh16 there are a few separate things going on here:
Note: I’m assuming you understand how inject
works and are confused by the :&
part.
TL DR;
inject(:&)
calls the &
method on all the values in your array. Under the hood, it’s evaluating: ((true & true) & true)
This should not be confused with a similar syntax that starts with &:
(note the reversed order) that takes advantage of Symbol#to_proc
and is commonly used by Rubyists instead of passing a block to a method.
Blocks
Many methods in Ruby allow you to pass them a block (inject
being one of them). For example, if you wanted to sum an array of numbers, you could do:
[1,2,3,4].inject do |running_total, number|
running_total + number
end
# => 10
Procs
Instead of passing a block, you can pass in a Proc
(Ruby’s function object) as an argument. When passing a function as an argument instead of using a block, we need to prefix it with &
.
sum = ->(x, y) { x + y }
# => #<Proc:0x007fc980c19160@(pry):4 (lambda)>
[1,2,3,4].inject(&sum)
#=> 10
Symbols
When passing an object instead of a block with &
, Ruby will call to_proc
on them before trying to evaluate the function. This means we can pass any object that responds to to_proc
. Symbol
is another type of object that responds to this method. Symbol#to_proc
returns a proc object that will delegate the method named by the symbol to it’s arguments. For example:
sum = :+.to_proc
# => #<Proc:0x007f890c1433d0(&:+)>
sum.call(1,2) # this is how you invoke a Proc object
# => 3
[1,2,3,4].inject(&sum)
# => 10
Since to_proc
gets called automatically, we can pass in symbols instead of procs:
[1,2,3,4].inject(&:+)
# => 10
The inject
method
Any method in Ruby that accepts a block can use the Symbol#to_proc
trick instead:
[1,2,3,4].map(&:even?)
# => [false, true, false, true]
[1,2,3,4].any?(&:negative?)
# => false
This style is commonly used with the various methods from the Enumerable
module. It is used throughout the article you linked.
However, inject
is special. From the docs (linked to by @trueheart78):
Combines all elements of enum by applying a binary operation, specified by a block or a symbol that names a method or operator.
Inject can take an optional symbol argument instead of a block. This means you can do:
[1,2,3,4].inject(:+)
=> 10
This is very similar to what we did before. However, notice that the argument is :+
, not :&:+
. The result is the same.
Your example
In addition to being used within method signatures to denote a block parameter, the &
is a method defined on TrueClass
and FalseClass
to implement boolean logic:
true & true
#=> true
true & false
#=> false
false & true
#=> false
false & false
# => false
In your example [true, true, true].inject(:&)
, the symbol :&
is passed to inject
. When using Symbol#to_proc
, the equivalent would be [true, true, true].inject(&:&)
. Both of these do the same thing: they AND all the values and return the result.
[true, true, true].inject(:&)
# => true
[true, true, true].inject(&:&)
# => true
Under the hood, it’s evaluating: ((true & true) & true)