Is inject the same thing as reduce in ruby?
Yes, and it's also called fold
in many other programming languages and in Mathematics. Ruby aliases a lot in order to be intuitive to programmers with different backgrounds. If you want to use #length
on an Array
, you can. If you want to use #size
, that's fine too!
how does reduce or inject work in this code
Suppose we execute
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :c)
so that within the method
default #=> 4
args #=> [:a, :b, :c]
self #=> { :a=>{:b=>{:c=>3 } } }
We then execute the following1:
args.empty? ? default : args.reduce(self) { |acum, key| acum.fetch(key) } rescue default
#=> [:a, :b, :c].empty? ? 4 : [:a, :b, :c].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
# acum.fetch(key) } rescue 4
#=> 3
If args #=> [:a, :b]
, we execute the following:
[:a, :b].empty? ? 4 : [:a, :b].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
acum.fetch(key) } rescue 4
#=> {:c=>3}
If args #=> [:a, :b, :cat]
, then a KeyError
exception is raised and the inline rescue
returns the value of default
:
[:a, :b, :cat].empty? ? 4 : [:a, :b, :cat].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
acum.fetch(key) } rescue 4
#=> 4
and if args #=> []
, [].empty?
is true
, so the value of default
is again returned:
[].empty? ? 4 : [].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
acum.fetch(key) } rescue 4
#=> 4
Fortunately, we no longer have to deal with such nonsense as we were given Hash#dig in Ruby 2.3.0, allowing us to write the following.
class Hash
def get_value( default, *keys )
keys.empty? ? default : dig(*keys) || default
end
end
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :c)
#=> 3
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b)
#=> {:c=>3}
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :cat)
#=> 4
{ :a=>{:b=>{:c=>3 } } }.get_value(4)
#=> 4
Note that the default receiver of dig
is self
.
1 Note that instead of ...args.reduce(self) { |acum, key| acum.fetch(key) } rescue default
the author of that code could have written ...args.reduce(self) { |acum, key| acum.fetch(key, default) }
. See Hash#fetch.
Deeper explanation of reduce / inject method in ruby
I had a similar issue with the default values in Ruby inject/reduce methods, so I've tried to visualize it:
Need a simple explanation of the inject method
You can think of the first block argument as an accumulator: the result of each run of the block is stored in the accumulator and then passed to the next execution of the block. In the case of the code shown above, you are defaulting the accumulator, result, to 0. Each run of the block adds the given number to the current total and then stores the result back into the accumulator. The next block call has this new value, adds to it, stores it again, and repeats.
At the end of the process, inject returns the accumulator, which in this case is the sum of all the values in the array, or 10.
Here's another simple example to create a hash from an array of objects, keyed by their string representation:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
In this case, we are defaulting our accumulator to an empty hash, then populating it each time the block executes. Notice we must return the hash as the last line of the block, because the result of the block will be stored back in the accumulator.
Rewriting Ruby #inject (#reduce) using recursion?
An alternative implementation
class Array
def aggregate(accumulator = nil, &sumator)
return accumulator if empty?
drop(1).aggregate(accumulator ? sumator.(accumulator, first) : first, &sumator)
end
end
As for should you worry about mutating the original - in short - yes. Usually methods in Ruby don't mutate objects if possible and make a copy. There are often times bang (!
) alternatives that do. Said "dangerous" methods mutate the original in place instead of returning a copy. However, in this situation this wouldn't make sense. The original is an array and the result is the sum.
As for mutating the original and returning the result separately, unless you have a real performance (or other) consideration, you shouldn't do it. It's unintuitive and can lead to confusing situations.
How to use reduce/inject in Ruby without getting Undefined variable
Block local variables
new_array
doesn't exist outside the block of your reduce
call. It's a "block local variable".
reduce
does return an object, though, and you should use it inside your method.
sum = [1, 2, 3].reduce(0){ |acc, elem| acc + elem }
puts sum
# 6
puts acc
# undefined local variable or method `acc' for main:Object (NameError)
Your code
Here's the least amount of change for your method :
def my_useless_function(str)
crazy_letters = ['a','s','d','f','g','h']
new_array = str.split(//).reduce([]) do |new_array, letter|
for a in 0..crazy_letters.length-1
if letter == crazy_letters[a]
new_array << letter
end
end
new_array
end
return true if (new_array == new_array.sort)
end
Notes:
return
isn't needed at the end.true if ...
isn't needed eitherfor
loop should never be used in Rubyreduce
returns the result of the last expression inside the block. It wasfor
in your code.- If you always need to return the same object in
reduce
, it might be a sign you could useeach_with_object
. "test".split
is just["test"]
String and Enumerable have methods that could help you. Using them, you could write a much cleaner and more efficient method, as in @Phrogz answer.
custom ruby inject method not producing expected output
I was able to solve it with code.
def my_inject(initial = nil, second = nil)
arr = is_a?(Array) ? self : to_a
sym = initial if initial.is_a?(Symbol) || initial.is_a?(String)
acc = initial if initial.is_a? Integer
if initial.is_a?(Integer)
sym = second if second.is_a?(Symbol) || second.is_a?(String)
end
if sym
arr.my_each { |x| acc = acc ? acc.send(sym, x) : x }
elsif block_given?
arr.my_each { |x| acc = acc ? yield(acc, x) : x }
end
acc
end
alias my_reduce my_injec
Related Topics
What Is the Correct Way to Detect If Ruby Is Running on Windows
Iconv Deprecation Warning with Ruby 1.9.3
Fast Way to Get Remote Image Dimensions
How to Get My MAChine's Ip Address from Ruby Without Leveraging from Other Ip Address
Ruby - Keyword Arguments - Can You Treat All of the Keyword Arguments as a Hash? How
Rmagick Remove White Background from Image and Make It Transparent
Ruby Loaderror: Cannot Load Such File
How to Sort Authors by Their Book Count with Activerecord
How to Append a String to a Variable That Either Exists or Not
Ruby Dropped in Netbeans 7,How to Use It in Netbeans7
Handling Namespace Models (Classes) in Namespace
Options for Distribution of an Offline Ruby on Rails Application
Error Installing Debugger: Failed to Build Gem Native Extension with Ruby-1.9.3-P362