How to use an enumerator - ruby

In the Ruby Array Class documentation, I often find:
If no block is given, an enumerator is returned instead.
Why would I not pass a block to #map? What would be the use of my doing just:
[1,2,3,4].map
instead of doing:
[1,2,3,4].map{ |e| e * 10 } # => [10, 20, 30, 40]
Can someone show me a very practical example of using this enumerator?

Good question.
What if we want to do multiple things with the enumerator that is created? We don't want to process it now, because it means we may need to create another later?
my_enum = %w[now is the time for all good elves to get to work].map # => #<Enumerator: ["now", "is", "the", "time", "for", "all", "good", "elves", "to", "get", "to", "work"]:map>
my_enum.each(&:upcase) # => ["NOW", "IS", "THE", "TIME", "FOR", "ALL", "GOOD", "ELVES", "TO", "GET", "TO", "WORK"]
my_enum.each(&:capitalize) # => ["Now", "Is", "The", "Time", "For", "All", "Good", "Elves", "To", "Get", "To", "Work"]

The main distinction between an Enumerator and most† other data structures in the Ruby core library (Array, Hash) and standard library (Set, SortedSet) is that an Enumerator can be infinite. You cannot have an Array of all even numbers or a stream of zeroes or all prime numbers, but you can definitely have such an Enumerator:
evens = Enumerator.new do |y|
i = -2
y << i += 2 while true
end
evens.take(10)
# => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
zeroes = [0].cycle
zeroes.take(10)
# => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
So, what can you do with such an Enumerator? Well, three things, basically.
Enumerator mixes in Enumerable. Therefore, you can use all Enumerable methods such as map, inject, all?, any?, none?, select, reject and so forth. Just be aware that an Enumerator may be infinite whereas map returns an Array, so trying to map an infinite Enumerator may create an infinitely large Array and take an infinite amount of time.
There are wrapping methods which somehow "enrich" an Enumerator and return a new Enumerator. For example, Enumerator#with_index adds a "loop counter" to the block and Enumerator#with_object adds a memo object.
You can use an Enumerator just like you would use it in other languages for external iteration by using the Enumerator#next method which will give you either the next value (and move the Enumerator forward) or raise a StopIteration exception if the Enumerator is finite and you have reached the end.
† Eg., an infinite range: (1..1.0/0)

The feature of returning a enumerable when no block is given is mostly used when chaining functions from the enumerable class together. Like this:
abc = %w[a b c]
p abc.map.with_index{|item, index| [item, index]} #=> [["a", 0], ["b", 1], ["c", 2]]
edit:
I think my own understanding of this behavior is a bit too limited in order to give a proper understanding of the inner workings of Ruby. I think the most important thing to note is that arguments are passed on in the same way they are for Procs. Thus if an array is passed in, it will be automatically 'splatted' (any better word for this?). I think the best way to get an understanding is to just use some simple functions returning enumerables and start experimenting.
abc = %w[a b c d]
p abc.each_slice(2) #<Enumerator: ["a", "b", "c", "d"]:each_slice(2)>
p abc.each_slice(2).to_a #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x| x} #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x,y| x+y} #=> ["ab", "cd"]
p abc.each_slice(2).map{|x,| x} #=> ["a", "c"] # rest of arguments discarded because of comma.
p abc.each_slice(2).map.with_index{|array, index| [array, index]} #=> [[["a", "b"], 0], [["c", "d"], 1]]
p abc.each_slice(2).map.with_index{|(x,y), index| [x,y, index]} #=> [["a", "b", 0], ["c", "d", 1]]

In addition to hirolau's answer, there is another method lazy that you can combine to modify the enumerator.
a_very_long_array.map.lazy
If map always took a block, then there would have to be another method like map_lazy, and similarly for other iterators to do the same thing.

I guess sometimes you want to pass the Enumerator to another method, despite where this Enumerator came from: map, slice, whatever:
def report enum
if Enumerator === enum
enum.each { |e| puts "Element: #{e}" }
else
raise "report method requires enumerator as parameter"
end
end
> report %w[one two three].map
# Element: one
# Element: two
# Element: three
> report (1..10).each_slice(2)
# Element: [1, 2]
# Element: [3, 4]
# Element: [5, 6]
# Element: [7, 8]
# Element: [9, 10]

Enumerator A class which allows both internal and external iteration
=> array = [1,2,3,4,5]
=> array.map
=> #<Enumerator: [2, 4, 6, 8, 10]:map>
=> array.map.next
=> 2
=> array.map.next_values
=> [0] 2

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.

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.

Partition array using index in ruby

I'm looking for an elegant way to partition an array by using index in ruby
eg:
["a","b",3,"c",5].partition_with_index(2)
=> [["a","b",3],["c",5]]
So far the best that I can think is using the below
["a","b",3,"c",5].partition.each_with_index{|val,index| index <= 2}
=> [["a","b",3],["c",5]]
Is there any other elegant way to accomplish this?
Thanks!
You can do:
["a","b",3,"c",5].partition.with_index { |_, index| index <= 2 }
Following #toro2k advice, I think this is a better solution because you are combining the two Enumerators to get the desired output.
If you don’t pass a block of code to partition, it returns an Enumerator object instead. Enumerators have a with_index method that will maintain the current loop index.
Why don't you use array.slice!
array#slice! Deletes the element(s) given by an index (optionally up to length elements) or by a range.
> a = ['a', 'b', 'c', 5]
> b = a.slice! 0, 2 # => ['a', 'b']
> a # => ['c', 5]
In your case,
> [a.slice!(0, index), a]
You could use Enumerable's take and drop methods:
a = ["a","b",3,"c",5]
[a.take(3), a.drop(3)] # => [["a", "b", 3], ["c", 5]]
I made an Enumerable quick reference sheet you might want to consult for questions like this.
This can be done, but not sure if it elegant or not :
a = ["a","b",3,"c",5]
index = 2
[a[0..index], a[index+1..-1]]
Thanks
You can try the below :
a = ["a","b",3,"c",5]
par = a.slice_before(sum: -2) do |elem, state|
state[:sum] += 1
state[:sum] == 2
end.to_a
par
# => [["a", "b", 3], ["c", 5]]
For your particular case, 'pyper' gem is usable:
require 'pyper' # gem install pyper if necessary
include Pyper
ary = ["a", "b", 3, "c", 5]
ary.τ3τ #=> ["a", "b", 3]
ary.τfτ #=> ["c", 5]
It only works easily on small n (number of chopped-off elements), but Pyper provides many other frequently encountered tasks on collections. It was inspired by lisp's car and cdr functions (see details by an anonymous donor), and the letters can be combined together into a control string, a bit like in APL. Greek tau (τ) is used to denote methods instead of c and r, so car, cdr become τaτ, τdτ:
ary.τaτ #=> "a"
ary.τdτ #=> ["b", 3, "c", 5]
# Instead of τfτ, one can write
ary.τdddτ #=> ["c", 5]
etc.

what does [0..1] mean in Module.constants() method?

In the following code
Module.constants[0..1] # => [:object, :Module]
What does the [0..1] mean here?
0..1 is a range. It's syntactic sugar for the Ruby parser to create a Range object. You can do a lot with ranges, including simple iteration:
irb(main):003:0> (1..3).class
=> Range
irb(main):004:0> (1..3).each {|x| puts x}
1
2
3
=> 1..3
You can turn it into an Array, among other things:
irb(main):005:0> (1..3).to_a
=> [1, 2, 3]
When you use a Range as an Array#[] argument, it means you want all the elements whose index is in that range (inclusive):
irb(main):007:0> stuff = %w{a b c d e f}
=> ["a", "b", "c", "d", "e", "f"]
irb(main):008:0> range = 2..4
=> 2..4
irb(main):009:0> stuff[range]
=> ["c", "d", "e"]
Module.constants returns an array of all the constants defined in (i.e. namespaced to) the Module class (yes, Module is a class; see Module.class). The [0..1] says give me every element of the array from the 0th to the 1st. In general, if x is an array, then x[m..n] returns the subarray of x consisting of the elements from the mth to the nth. For example:
x = [36, 25, 16, 9, 4]
x[1..3] # => [25, 16, 9]

Weird multiplicator operator behavior in a two arrays to hash combination

I was looking for a way to convert two arrays into a single hash. I found something like this :
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
Hash[*a1.zip(a2).flatten]
I thought that this syntax was a bit weird, because Hash[a1.zip a2] would do exactly the same. But more than that, I don't understand the need for the * operator.
I know that it turns objects into arrays, or something alike (but not in the same way [] does, apparently).
When I execute :
a = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
a = *a1.zip(a).flatten
=> [1, "A", 2, "B", 3, "C"]
Nothing really happens, and for what I know of the * operator, this seems to be the normal behavior.
So, why does
Hash[*a1.zip(a2).flatten]
=> {1=>"A", 2=>"B", 3=>"C"}
Hash[a1.zip(a).flatten]
=> {}
Return different values, given that the parameters seem identical ?
I guess I must be missing something about the * operator.
Thanks.
When the * operator is used with arrays like that it is called the splat operator.
Think of it as an operator that removes the first level of brackets around an array. This is quite useful because you can turn arrays into argument lists:
def stuff(x, y, z)
end
a = [1, 2, 3]
stuff(*a) # x,y,z gets assigned 1,2,3
The same thing works with Hash[]. The [] operator on Hash accepts as arguments:
An argument list of key-value pairs:
Hash["a", 1, "b", 2] #=> { "a" => 1, "b" => 2 }
An array or array pairs representing key-values:
Hash[ [["a", 1], ["b", 2]] ] #=> { "a" => 1, "b" => 2 }
Hash[] not does NOT accept a plain flat array as arguments:
Hash[ ["a", 1, "b", 2] ] #=> {}
So with this in mind, plus our understanding what the splat operator does you can now see what is happening:
paired_array = a1.zip(a2)
=> [[1, "A"], [2, "B"], [3, "C"]]
plain_array = a1.zip(a2).flatten
=> [1, "A", 2, "B", 3, "C"]
# Per rule 2 above we know this works
Hash[paired_array]
=> {1=>"A", 2=>"B", 3=>"C"}
# This won't work
Hash[plain_array]
=> {}
# But if we turn the plain_array into an argument list,
# then we know per rule 1 above that this will work
Hash[*plain_array]
=> {1=>"A", 2=>"B", 3=>"C"}
Now then you might be wondering what the hey is happening when you do:
a = *plain_array
=> [1, "A", 2, "B", 3, "C"]
Since we know the splat operator effectively strips the brackets, we get this:
a = 1, "A", 2, "B", 3, "C"
...which funnily enough is valid Ruby code and just creates an array again.
You can read more fun stuff about the splat operator in the rubyspec test case for the splat operator.
I think there's a mistake in your example, it should be like this:
Hash[a1.zip(a2).flatten] #=> {}
Hash[*a1.zip(a2).flatten] #=> {1=>"A", 2=>"B", 3=>"C"}
The splat operator in the assign mode converts an array to multiple arguments:
duck, cow, pig = *["quack","mooh","oing"] #=> ["quack","mooh","oing"]
Actually it's identical to
duck, cow, pig = ["quack","mooh","oing"] #=> ["quack","mooh","oing"]
But from the documentation you can see that Hash[...] receives multiple arguments, so the splat operator helps to assign each of those multiple arguments.
It's not that mysterious:
a1 = [1,2,3]
a2 = [?A, ?B, ?C]
p Hash[*a1.zip(a2).flatten] #{1=>"A", 2=>"B", 3=>"C"}
The * converts the array to a mere list (of arguments).
But why wasn't this syntax used?
p Hash[a1.zip(a2)]# {1=>"A", 2=>"B", 3=>"C"}
Well, it is new since Ruby 1.9.2. Your example is probably older.

Resources