ruby syntax code involving hashes - ruby

I was looking at code regarding how to return a mode from an array and I ran into this code:
def mode(array)
answer = array.inject ({}) { |k, v| k[v]=array.count(v);k}
answer.select { |k,v| v == answer.values.max}.keys
end
I'm trying to conceptualize what the syntax means behind it as I am fairly new to Ruby and don't exactly understand how hashes are being used here. Any help would be greatly appreciated.

Line by line:
answer = array.inject ({}) { |k, v| k[v]=array.count(v);k}
This assembles a hash of counts. I would not have called the variable answer because it is not the answer, it is an intermediary step. The inject() method (also known as reduce()) allows you to iterate over a collection, keeping an accumulator (e.g. a running total or in this case a hash collecting counts). It needs a starting value of {} so that the hash exists when attempting to store a value. Given the array [1,2,2,2,3,4,5,6,6] the counts would look like this: {1=>1, 2=>3, 3=>1, 4=>1, 5=>1, 6=>2}.
answer.select { |k,v| v == answer.values.max}.keys
This selects all elements in the above hash whose value is equal to the maximum value, in other words the highest. Then it identifies the keys associated with the maximum values. Note that it will list multiple values if they share the maximum value.
An alternative:
If you didn't care about returning multiple, you could use group_by as follows:
array.group_by{|x|x}.values.max_by(&:size).first
or, in Ruby 2.2+:
array.group_by{&:itself}.values.max_by(&:size).first

The inject method acts like an accumulator. Here is a simpler example:
sum = [1,2,3].inject(0) { |current_tally, new_value| current_tally + new_value }
The 0 is the starting point.
So after the first line, we have a hash that maps each number to the number of times it appears.
The mode calls for the most frequent element, and that is what the next line does: selects only those who are equal to the maximum.

I believe your question has been answered, and #Mark mentioned different ways to do the calculations. I would like to just focus on other ways to improve the first line of code:
answer = array.inject ({}) { |k, v| k[v] = array.count(v); k }
First, let's create some data:
array = [1,2,1,4,3,2,1]
Use each_with_object instead of inject
My suspicion is that the code might be fairly old, as Enumerable#each_with_object, which was introduced in v. 1.9, is arguably a better choice here than Enumerable#inject (aka reduce). If we were to use each_with_object, the first line would be:
answer = array.each_with_object ({}) { |v,k| k[v] = array.count(v) }
#=> {1=>3, 2=>2, 4=>1, 3=>1}
each_with_object returns the object, a hash held by the block variable v.
As you see, each_with_object is very similar to inject, the only differences being:
it is not necessary to return v from the block to each_with_object, as it is with inject (the reason for that annoying ; v at the end of inject's block);
the block variable for the object (k) follows v with each_with_object, whereas it proceeds v with inject; and
when not given a block, each_with_object returns an enumerator, meaning it can be chained to other other methods (e.g., arr.each_with_object.with_index ....
Don't get me wrong, inject remains an extremely powerful method, and in many situations it has no peer.
Two more improvements
In addition to replacing inject with each_with_object, let me make two other changes:
answer = array.uniq.each_with_object ({}) { |k,h| h[k] = array.count(k) }
#=> {1=>3, 2=>2, 4=>1, 3=>1}
In the original expression, the object returned by inject (sometimes called the "memo") was represented by the block variable k, which I am using to represent a hash key ("k" for "key"). Simlarly, as the object is a hash, I chose to use h for its block variable. Like many others, I prefer to keep the block variables short and use names that indicate object type (e.g., a for array, h for hash, s for string, sym for symbol, and so on).
Now suppose:
array = [1,1]
then inject would pass the first 1 into the block and then compute k[1] = array.count(1) #=> 2, so the hash k returned to inject would be {1=>2}. It would then pass the second 1 into the block, again compute k[1] = array.count(1) #=> 2, overwriting 1=>1 in k with 1=>1; that is, not changing it at all. Doesn't it make more sense to just do this for the unique values of array? That's why I have: array.uniq....
Even better: use a counting hash
This is still quite inefficient--all those counts. Here's a way that reads better and is probably more efficient:
array.each_with_object(Hash.new(0)) { |k,h| h[k] += 1 }
#=> {1=>3, 2=>2, 4=>1, 3=>1}
Let's have a look at this in gory detail. Firstly, the docs for Hash#new read, "If obj is specified [i.e., Hash.new(obj)], this single object will be used for all default values." This means that if:
h = Hash.new('cat')
and h does not have a key dog, then:
h['dog'] #=> 'cat'
Important: The last expression is often misunderstood. It merely returns the default value. str = "It does *not* add the key-value pair 'dog'=>'cat' to the hash." Let me repeat that: puts str.
Now let's see what's happening here:
enum = array.each_with_object(Hash.new(0))
#=> #<Enumerator: [1, 2, 1, 4, 3, 2, 1]:each_with_object({})>
We can see the contents of the enumerator by converting it to an array:
enum.to_a
#=> [[1, {}], [2, {}], [1, {}], [4, {}], [3, {}], [2, {}], [1, {}]]
These seven elements are passed into the block by the method each:
enum.each { |k,h| h[k] += 1 }
=> {1=>3, 2=>2, 4=>1, 3=>1}
Pretty cool, eh?
We can simulate this using Enumerator#next. The first value of enum ([1, {}]) is passed to the block and assigned to the block variables:
k,h = enum.next
#=> [1, {}]
k #=> 1
h #=> {}
and we compute:
h[k] += 1
#=> h[k] = h[k] + 1 (what '+=' means)
# = 0 + 1 = 1 (h[k] on the right equals the default value
# of 1 since `h` has no key `k`)
so now:
h #=> {1=>1}
Next, each passes the second value of enum into the block and similar calculations are performed:
k,h = enum.next
#=> [2, {1=>1}]
k #=> 2
h #=> {1=>1}
h[k] += 1
#=> 1
h #=> {1=>1, 2=>1}
Things are a little different when the third element of enum is passed in, because h now has a key 1:
k,h = enum.next
#=> [1, {1=>1, 2=>1}]
k #=> 1
h #=> {1=>1, 2=>1}
h[k] += 1
#=> h[k] = h[k] + 1
#=> h[1] = h[1] + 1
#=> h[1] = 1 + 1 => 2
h #=> {1=>1, 2=>1}
The remaining calculations are performed similarly.

Related

Reduce hash with key, value and index as block parameters

h = { "a" => 1, "b" => 2 }
Is there a way to reduce a hash and have the key, value and index as block parameters?
As a starting point I can iterate over a hash getting key, value and index:
h.each_with_index { |(k,v), i| puts [k,v,i].inspect }
# => ["a", 1, 0]
# => ["b", 2, 1]
However when I add reduce I seem to loose the ability to have the key and value as separate values and instead they are provided as a two element array:
h.each_with_index.reduce([]) { |memo, (kv,i)| puts [kv,i].inspect }
# => [["a", 1], 0]
# => [["b", 2], 1]
This is okay, I can in the block do kv[0] and kv[1], but I'd like something like this:
h.each_with_index.reduce([]) { |memo, (k,v), i| puts [k,v,i].inspect }
I'd like to do this without monkey-patching.
Maybe something like this?:
h.each_with_index.reduce([]) { |memo, ((k,v), i)| puts [k,v,i].inspect }
#=> ["a", 1, 0]
#=> ["b", 2, 1]
#=> nil
All you need is scoping: ((k,v), i).
Keeping in mind with reduce, we always have to return the object at the end of block. Which is kind of an extra overhead unless last operation isn't on the memo object which returns the object itself.Otherwise it won't return the desired result.
Same thing can be achieved with each_with_index chained with with_object like so:
h.each_with_index.with_object([]) { |((k,v), i), memo| memo << [k,v,i].inspect }
#=> ["a", 1, 0]
#=> ["b", 2, 1]
#=> []
See the array at last line of output? That's our memo object, which isn't same as reduce that we used above.
When in doubt what the block arguments are, create an instance of an Enumerator and call #next on it:
▶ h = {a: 1, b: 2}
#⇒ {:a=>1, :b=>2}
▶ enum = h.each.with_index.with_object([])
#⇒ #<Enumerator: ...>
▶ enum.next
#⇒ [[[:a, 1], 0], []]
The returned value consists of:
array of key and value, joined into:
array with an index, joined into:
array with an accumulator (for reduce it’d go in front, if reduce returned an enumerator when called without a block—credits to #Stefan for nitpicking.)
Hence, the proper parentheses for decomposing it would be:
# ⇓ ⇓ ⇓ ⇓
# [ [ [:a, 1], 0 ], [] ]
{ | ( (k, v), idx ), memo| ...
Enumerable#each_with_index yields two values into the block: the item and its index. When it is invoked for a Hash, the item is an array that contains two elements: the key and the associated value.
When you declare the block arguments |(k,v), i| you, in fact, deconstruct the first block argument (the item) into its two components: the key and the value. Without a block h.each_with_index produces an Enumerator that yields both arguments of the previously used block wrapped into an array.
This array is the second argument of Enumerator#reduce.
You can tell this by running:
irb> h.each_with_index.reduce([]) { |memo, j| p j }
[["a", 1], 0]
[["b", 2], 1]
Now, the answer to your question is easy: just deconstruct j and you get:
irb> h.each_with_index.reduce([]) { |memo, ((k,v), i)| puts [k,v,i].inspect }
["a", 1, 0]
["b", 2, 1]
Of course, you should memo << [k,v,i] or put the values in memo using other other rules and return memo to get your final desired result.

Use case of each_with_index vs index in Ruby

New to Ruby; When I am working with arrays, is there a difference between Enumerable#each_with_index and Array#index I have been working with a multidimensional array in which I am trying to find the location of a given value. My code is able to pass all the tests when I use either one. Coordinates is the array I am using to hold the location of the given value (in this case 1)
field=[[1,0],[0,0]]
coordinates=[]
field.each_with_index do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
Thanks in advance for the help.
Let's take a closer look at your code:
field=[[1,0],[0,0]]
coordindates = []
field.each_with_index do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
Let:
enum = field.each_with_index
#=> #<Enumerator: [[1, 0], [0, 0]]:each_with_index>
As you see this returns an enumerator.
Ruby sees your code like this:
enum.each do |item|
if item.index(1)
coordinates.push(field.index(item)).push(item.index(1))
end
end
The elements of the enumerator will be passed into the block by Enumerator#each, which will call Array#each since the receiver, field is an instance of the class Array.
We can see the elements of enum by converting it to an array:
enum.to_a
#=> [[[1, 0], 0], [[0, 0], 1]]
As you see, it has two elements, each being an array of two elements, the first being an array of two integers and the second being an integer.
We can simulate the operation of each by sending Enumerator#next to enum and assigning the block variables to the value returned by next. As there is but one block variable, item, we have:
item = enum.next
#=> [[1, 0], 0]
That is quite likely neither what you were expecting nor what you wanted.
Next, you invoke Array#index on item:
item.index(1)
#=> nil
index searches the array item for an element that equals 1. If it finds one it returns that element's index in the array. (For example, item.index(0) #=> 1). As neither [1,0] nor 0 equals 1, index returns nil.
Let's rewind (and recreate the enumerator). You need two block variables:
field.each_with_index do |item, index|...
which is the same as:
enum.each do |item, index|...
So now:
item, index = enum.next
#=> [[1, 0], 0]
item #=> [1, 0]
index #=> 0
and
item.index(1)
#=> 0
I will let you take it from here, but let me mention just one more thing. I'm not advocating it, but you could have written:
field.each_with_index do |(first, second), index|...
in which case:
(first, second), index = enum.next
#=> [[1, 0], 0]
first #=> 1
second #=> 0
index #=> 0
See Ruby doc: http://ruby-doc.org/core-2.2.0/Array.html#method-i-index and http://ruby-doc.org/core-2.2.1/Enumerable.html#method-i-each_with_index
index is a method of Array, it detects whether an item exists in a specific Array, return the index if the item exists, and nil if does not exist.
each_with_index is a method of Enumerable mixin, it usually takes 2 arguments, first one is item and the second one is index.
So your sample could be simplified as:
field = [[1, 0], [0, 0]]
coordinates = []
field.each_with_index do |item, index|
item_index = item.index(1)
coordinates << index << item_index if item_index
end
puts coordinates.inspect # => [0, 0]
Note your field.index(item) is just index.
Array#index and Enumerable#each_with_index are not related whatsoever (functionally speaking), one is used to get the index of an object within an Array and the other one is used to walk through a collection.
Actually each_with_index is a mutation of the each method that additionally yields the index of the actual object within the collection. This method is very useful if you need to keep track of the current position of the object you are in. It saves you the trouble of creating and incrementing an additional variable.
For example
['a', 'b', 'c', 'd'].each_with_index do|char, idx|
puts "#{idx}) #{char}"
end
output:
0) a
1) b
2) c
3) d
Without each_with_index
idx = 0
['a', 'b', 'c', 'd'].each do|char|
puts "#{idx}) #{char}"
idx += 1
end
output:
0) a
1) b
2) c
3) d
To find the index of an object within a multidimensional Array you will have to iterate at least through all the rows of the matrix, like this (using each_with_index and index)
def find_position(matrix, obj)
matrix.each_with_index do|row, i|
return [i, j] if j = row.index(obj)
end
return nil
end
for example:
find_position([[2,3],[4,5]], 5) # => [1, 1]
Given that:
fields = [[1, 0], [0, 0]]
Enumerable#each_with_index passes each index as well as each object to the block:
fields.each_with_index do |field, idx|
puts "#{field}, #{idx}"
end
#=> [1, 0], 0
#=> [0, 0], 1
Array#index returns the index of the matching array element:
puts fields.index([1, 0])
#=> 0

I don't know why this Ruby Fibonacci sequence works

I'm writing a program that pushes Fibonacci numbers into an array, using Ruby. The code works, but I can't wrap my head around why it works.
This part I understand, it's the Fibonacci equation:
fib_array = []
def fib (n)
return n if n <= 1
fib(n - 1) + fib(n - 2)
end
This is what I don't understand:
10.times do |x|
fib_array << fib(x)
end
print fib_array
I wrote this grasping at straws, and it works. I don't understand why. I didn't feed it a number to start at, does Ruby take that to mean 0? Also, how did it know to compound the numbers instead of printing [0, 0, 0...]? I apologize if this is a dunderheaded question, but I'm at loss.
It looks like the bottom piece of code simply calls the fib function on x=0, x=1 ... x=9 and stores it's return value at the end of the array. When times is invoked with an iteration variable (x), it begins at 0 and increments on each iteration through the loop. You never fed it a value, however it manages to successfully solve the problem with the iteration variable x being passed in as the parameter to fib.
The second part of your code says:
"From the instance 10 of the class Integer, call the method times with the given block" (The method "recive" a block implicitly).
What is a block? A small piece of code between {braces} or a do-end (like you did).
The method times is called, "iterator". And it will yield 0,1,2,..,9 (in your case). An iterator and the yield statement are always together. Think that yield is like return with memory, when you look for more information.
So, your code could be re-writing like:
10.times { |x| fib_array << fib(x) }
And it will call, the block you pass, on every yield that the method times
does. Calling the method << (append) to the result of fib(x) on your array.
We have:
def fib (n)
return n if n <= 1
fib(n - 1) + fib(n - 2)
end
which you understand. First let's see what are the first 5 Fibonacci numbers::
fib_array = []
5.times do |x|
fib_array << fib(x)
end
fib_array
#=> [0, 1, 1, 2, 3]
Now let's break this down and see what happening. First look at the docs for the method times. To find them, we need to know what class or module the method is from, because that's how the docs are organized. As 5.class #=> Fixnum, we might look at the docs for Fixnum. Hmmm. times is not there. Evidentally, it was inherited from another class. Let's check:
Fixnum.ancestors
#=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]
Integer includes Fixnum and BigNum, Numeric includes Integer and Float. (3.14).times doesn't make sense, so it appears times is defined in Integer, and so it is: Integer#times. By defining it there, it is inherited by both Fixnum and Bignum.
Here's a direct way to determine where the method came from:
5.method(:times).owner #=> Integer
It's not important understand this now, but you'll find it handy as you gain experience with Ruby.
OK, the docs say that times return a value if given a block, or an enumerator if not. Let's forget about the block a moment and look at the enumerator that is returned:
enum = 5.times #=> #<Enumerator: 5:times>
The method Enumerator#each passes the elements of the enumerator enum to its block:
do |x|
fib_array << fib(x)
end
assigning them to the block variable x. To see the contents of the enumerator, convert it to an array:
enum.to_a #=> [0, 1, 2, 3, 4]
The result is:
fib_array = []
enum.each do |x|
fib_array << fib(x)
end
fib_array
#=> [0, 1, 1, 2, 3]
which of course is the same result that we obtained previously. Now let's see what's happening step-by-by, by using the method Enumerator#next to extract each element of the enumerator:
x = enum.next #=> 0
fib(x) #=> 0
fib_array << fib(x) #=> [0]
x = enum.next #=> 1
fib(x) #=> 1
fib_array << fib(x) #=> [0, 1]
x = enum.next #=> 2
fib(x) #=> 1
fib_array << fib(x) #=> [0, 1, 1]
x = enum.next #=> 3
fib(x) #=> 2
fib_array << fib(x) #=> [0, 1, 1, 2]
x = enum.next #=> 4
fib(x) #=> 3
fib_array << fib(x) #=> [0, 1, 1, 2, 3]
print fib_array # [0, 1, 1, 2, 3]
That's all there is to it.

Swap hash keys with values and convert keys to symbols in Ruby?

This is the input hash:
p Score.periods #{"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
This is my current code to exchange the keys with the values, while converting the keys to symbols:
periods = Score.periods.inject({}) do |hsh,(k,v)|
hsh[v] = k.to_sym
hsh
end
Here is the result:
p periods #{0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
It just seems like my code is clunky and it shouldn't take 4 lines to do what I'm doing here. Is there a cleaner way to write this?
You can do this:
Hash[periods.values.zip(periods.keys.map(&:to_sym))]
Or if you're using a version of Ruby where to_h is available for arrays, you can do this:
periods.values.zip(periods.keys.map(&:to_sym)).to_h
What the two examples above do is make arrays of the keys and values of the original hash. Note that the string keys of the hash are mapped to symbols by passing to_sym to map as a Proc:
periods.keys.map(&:to_sym)
# => [:q1, :q2, :q3, :q4, :h1, :h2]
periods.values
# => [0, 1, 2, 3, 4, 5]
Then it zips them up into an array of [value, key] pairs, where each corresponding elements of values is matched with its corresponding key in keys:
periods.values.zip(periods.keys.map(&:to_sym))
# => [[0, :q1], [1, :q2], [2, :q3], [3, :q4], [4, :h1], [5, :h2]]
Then that array can be converted back into a hash using Hash[array] or array.to_h.
The simplest way is:
data = {"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
Hash[data.invert.collect { |k, v| [ k, v.to_sym ] }]
The Hash[] method converts an array of key/value pairs into an actual Hash. Quite handy for situations like this.
If you're using Ruby on Rails this could be even easier:
data.symbolize_keys.invert
h = {"q1"=>0, "q2"=>1, "q3"=>2, "q4"=>3, "h1"=>4, "h2"=>5}
h.each_with_object({}) { |(k,v),g| g[v] = k.to_sym }
#=> {0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
The steps are as follows (for the benefit of Ruby newbies).
enum = h.each_with_object({})
#=> #<Enumerator: {0=>"q1", 1=>"q2", 2=>"q3", 3=>"q4",
# 4=>"h1", 5=>"h2"}:each_with_object({})>
The elements that will be generated by the enumerator and passed to the block can be seen by converting the enumerator to an array, using Enumerable#entries or Enumerable#to_a.
enum.entries
#=> [[["q1", 0], {}], [["q2", 1], {}], [["q3", 2], {}],
# [["q4", 3], {}], [["h1", 4], {}], [["h2", 5], {}]]
Continuing,
enum.each { |(k,v),g| g[v] = k.to_sym }
#=> {0=>:q1, 1=>:q2, 2=>:q3, 3=>:q4, 4=>:h1, 5=>:h2}
In the last step, Enumerator#each passes the first element generated by enum to the block and assigns the three block variables. Consider the first element of enum that is passed to the block and the associated calculation of values for the three block variables. (I must first execute enum.rewind to reinitialize enum, as each above took the enumerator to its end. See Enumerator#rewind).
(k, v), g = enum.next
#=> [["q1", 0], {}]
k #=> "q1"
v #=> 0
g #=> {}
See Enumerator#next. The block calculation is therefore
g[v] = k.to_sym
#=> :q1
Hence,
g #=> {0=>:q1}
The next element of enum is passed to the block and similar calculations are performed.
(k, v), g = enum.next
#=> [["q2", 1], {0=>:q1}]
k #=> "q2"
v #=> 1
g #=> {0=>:q1}
g[v] = k.to_sym
#=> :q2
g #=> {0=>:q1, 1=>:q2}
The remaining calculations are similar.

Skip over iteration in Enumerable#collect

(1..4).collect do |x|
next if x == 3
x + 1
end # => [2, 3, nil, 5]
# desired => [2, 3, 5]
If the condition for next is met, collect puts nil in the array, whereas what I'm trying to do is put no element in the returned array if the condition is met. Is this possible without calling delete_if { |x| x == nil } on the returned array?
My code excerpt is heavily abstracted, so looking for a general solution to the problem.
There is method Enumerable#reject which serves just the purpose:
(1..4).reject{|x| x == 3}.collect{|x| x + 1}
The practice of directly using an output of one method as an input of another is called method chaining and is very common in Ruby.
BTW, map (or collect) is used for direct mapping of input enumerable to the output one. If you need to output different number of elements, chances are that you need another method of Enumerable.
Edit: If you are bothered by the fact that some of the elements are iterated twice, you can use less elegant solution based on inject (or its similar method named each_with_object):
(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
I would simply call .compact on the resultant array, which removes any instances of nil in an array. If you'd like it to modify the existing array (no reason not to), use .compact!:
(1..4).collect do |x|
next if x == 3
x
end.compact!
In Ruby 2.7+, it’s possible to use filter_map for this exact purpose. From the docs:
Returns an array containing truthy elements returned by the block.
(0..9).filter_map {|i| i * 2 if i.even? } #=> [0, 4, 8, 12, 16]
{foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } #=> [:foo, :baz]
For the example in the question: (1..4).filter_map { |x| x + 1 unless x == 3 }.
See this post for comparison with alternative methods, including benchmarks.
just a suggestion, why don't you do it this way:
result = []
(1..4).each do |x|
next if x == 3
result << x
end
result # => [1, 2, 4]
in that way you saved another iteration to remove nil elements from the array. hope it helps =)
i would suggest to use:
(1..4).to_a.delete_if {|x| x == 3}
instead of the collect + next statement.
You could pull the decision-making into a helper method, and use it via Enumerable#reduce:
def potentially_keep(list, i)
if i === 3
list
else
list.push i
end
end
# => :potentially_keep
(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]

Resources