Did the `each` method change its behavior? - ruby

A Ruby exercise about multidimensional array said that two instances of each method are necessary to access the inner elements of a multidimensional array. The following:
x = [[1,2],[3,4],[5,6]]
x.each do |a|
a.each do |b|
puts b
end
end
should return:
# 1
# 2
# 3
# 4
# 5
# 6
However, it's not necessary to use two each methods. If I just do
x.each { |a| puts a }
I get the same result. It seems a single instance of each already goes to the inner level of multidimensional arrays.
In that case, how would I access the first level? In other words, how would I get the following?
# [1,2]
# [3,4]
# [5,6]

There are three different print functions in Ruby. Let's try them in the Ruby prompt:
> puts [1,2]
1
2
=> nil
> p [1,2]
[1, 2]
=> [1, 2]
> print [1,2]
[1, 2]=> nil
In case you aren't familiar with irb, the expression following the fat arrow => is the return value of the statement.

Moreover, if you do just
puts x
you'll get exactly the same result. This is because puts treats arrays in a special manner. It enumerates all elements and calls puts on them individually. (this is recursive, as you might imagine).
This will get roughly the output you want:
x.each {|a| p a}
or
x.each {|a| puts a.inspect }
Output
# >> [1, 2]
# >> [3, 4]
# >> [5, 6]

x.each { |a| puts a }
This will call puts on each element of the x array.
It is the same as doing :
puts [1,2]
puts [3,4]
puts [5,6]
puts on an array will format it like you saw.

Related

A single method to delete! elements from Array by predicate and return deleted

I couldn't find a method in docs to do the following:
a = [1, 2, 3]
b = a.remove! {|x| x > 1}
puts a # [1]
puts b # [2, 3]
There is a select! method that does similar thing but it doesn't accept a predicate.
To my disappointment, delete_if, keep_if, reject! and select! mutate the array but also return the same array.
Currently, I achieve a desired in 2 steps like this but maybe there are better/smarter options?
a = [1, 2, 3]
b = a.reject {|x| x > 1}
a = a - b
puts a # [1]
puts b # [2, 3]
I don't know a way to accomplish that in 1 step, however if as in your 2nd example you'd accept to not mutate a from the ruby method, you could use Enumerable#partition
a = [1, 2, 3]
# note that b is first because first array is for elements returning true
b, a = a.partition { |x| x > 1 }
puts a # [1]
puts b # [2, 3]

How can I make a ruby enumerator that does lazy iteration through two other enumerators?

Let's say I have two enumerators, enum1 and enum2 that must be lazily iterated through (because they have side effects). How do I construct a third enumerator enum3 where enum3.each{|x| x} would lazily return the equivalent of enum1 + enum2?
In my real world use case, I'm streaming in two files, and need to stream out the concatenation.
This seems to work just how I want;
enums.lazy.flat_map{|enum| enum.lazy }
Here's the demonstration. Define these yielding methods with side-effects;
def test_enum
return enum_for __method__ unless block_given?
puts 'hi'
yield 1
puts 'hi again'
yield 2
end
def test_enum2
return enum_for __method__ unless block_given?
puts :a
yield :a
puts :b
yield :b
end
concated_enum = [test_enum, test_enum2].lazy.flat_map{|en| en.lazy }
Then call next on the result, showing that the side effects happen lazily;
[5] pry(main)> concated_enum.next
hi
=> 1
[6] pry(main)> concated_enum.next
hi again
=> 2
Here's some code I wrote for fun awhile back with lazy enumeration thrown in:
def cat(*args)
args = args.to_enum
Enumerator.new do |yielder|
enum = args.next.lazy
loop do
begin
yielder << enum.next
rescue StopIteration
enum = args.next.lazy
end
end
end
end
You would use it like this:
enum1 = [1,2,3]
enum2 = [4,5,6]
enum3 = cat(enum1, enum2)
enum3.each do |n|
puts n
end
# => 1
# 2
# 3
# 4
# 5
# 6
...or just:
cat([1,2,3],[4,5,6]).each {|n| puts n }
Since Ruby 2.6 you can use Enumerable#chain/Enumerator::Chain:
a = [1, 2, 3].lazy
b = [4, 5, 6].lazy
a.chain(b).to_a
# => [1, 2, 3, 4, 5, 6]
Enumerator::Chain.new(a, b).to_a
# => [1, 2, 3, 4, 5, 6]

Iterator calls all elements at once within an each block

I build the array here:
def initialize
#names = []
end
#names << page.all('//*[#id="USERS_AVAIL"]/option').map {|result| result.text.split(", ")}
later on I'm trying to compile and visit url's by iterating through the names array like so:
#names.each do |name|
visit "https://example.com/k=#{name}&tpe=1"
end
Some puts statements show me that the each method is calling every element of the array all at once instead of iterating as intended. I.E.: "https://example.com/k=#{[[%22Adviento%22,%20%22Justin%22],%20[%22Asamoah%22,%20%22Nathan%22],%20[%22Baughman%22,%20%22Zachary%22],}&tpe=1". #names.length has a count of only 4 but a puts of the #names array shows the proper output? I'm not sure what could be wrong, thanks in advance for any assist.
Replace << with +=. The << is inserting the entire array as a single element of its own, whereas += will concatenate the array, which seems to be your intention.
For example:
a = [1,2,3]
# => [1, 2, 3]
a << [4,5,6]
# => [1, 2, 3, [4, 5, 6]] # WRONG
a = [1,2,3]
# => [1, 2, 3]
a += [4,5,6]
# => [1, 2, 3, 4, 5, 6] # CORRECT
Try:
#names += page.all('//*[#id="USERS_AVAIL"]/option')
.map { |r| r.text.split(',').map(&:strip) }.flatten
If the quotes are in the literal form %22 and you want to capture the strings in between them:
#names += page.all('//*[#id="USERS_AVAIL"]/option')
.map { |r| r.text.scan(/%22([^%]+)%22/) }.flatten

How do you iterate through an amalgamation of two Enumerables efficiently?

Given
a = nil # or [1,2]
b = [1,2] # or nil
Can you iterate through the concatenation of a and b without allocating an intermediate or creating massive amount of boiler plate code?
# meaning do this much more efficiently
((a || []) + (b || [])).each do |thing|
# more lines here
puts thing
end
This is kind of ugly:
l = lambda{|thing| do_my_thing }
a.each{|thing| l.call(thing)} if a
b.each{|thing| l.call(thing)} if b
Well, if you're willing to create a container (which should be cheap even if the elements contained are large), you could:
[a,b].compact.each do |e|
e.each do
# stuff
end
end
You do have to create a container array, but since you don't have to copy the contents of the sub-arrays (and instead are just dealing with two array pointers), it should be very quick and not terrible on the GC.
An alternate solution might just be to create a method:
def multi_each(*args,&block)
args.each do |a|
a.each(&block) if a
end
end
multi_each(nil,[1],[2,3]) do |i|
puts i
end
# 1
# 2
# 3
If you are using 1.9, I would use the ability for multiple splats:
a = nil
b = [1, 2]
[*a, *b]
#=> [1, 2]
a = [3, 4]
b = nil
[*a, *b]
#=> [3, 4]
So [*a, *b].each {} seems exactly like what you want.
What you have can be made far more concise and considerably less ugly by passing the lambda as the block itself:
l = lambda { |thing| do_my_thing }
a.each(&l) if a
b.each(&l) if b

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