Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Enumerator Chain

Initially I was going to do something like below:

arr = [[1,2],[3,4]]
new_arr = 
arr.map do |sub_arr|
    sub_arr.map do |x|
        x+1
    end
end

p new_arr

Output:

[[2,3],[4,5]]

But then I tried to short it by "chaining up" the enumerators:

arr.map.map{|x| x+1}

Then it gives error to_ary method missing

I debugged it by

arr.each.each{|x| p x}

Output:

[1,2]
[3,4]

,which is the original array and only desected once.

How can I chain two map/each enumerators so it will enumerator into 2 (or more) levels? Or it has to be in the block?


Update:

After some searching, apparently a chain obj.Enumerator.Enumerator.Enumerator... only enumerates the obj once, and only 1 level depth. In order to go deeper, block is required. I worked out simple code that would convert string to block(Proc/Lambda; similar to symbol to block but more of use; more like a functional syntax) so that block will be avoided. Someone has similar code String#to_proc but I couldn't find it, and the x,y thing in it does not appeal to my taste. I use $0,$1,$2,...

Sample code (the previous example will be written as):

arr = [[1,2],[3,4]]
new_arr = arr.map(&'[$0+1,$1+1]')
p new_arr

I will push the original code to github later. You can use chat to contact me if you want to see it before that because I really procrastinate :)

like image 602
SwiftMango Avatar asked Feb 20 '23 19:02

SwiftMango


2 Answers

Perhaps you need a map which you would like to apply only on the leafs:

module Enumerable
  def nested_map &block
    map{|e|
      case e
      when Enumerable
        e.nested_map(&block)
      else
        block.call(e)
      end
    }
  end
end

p [[1,2], [3,4]].nested_map(&:succ)
#=> [[2, 3], [4, 5]]

or a map which would apply only on n-th level of the nested structure.

module Enumerable
  def deep_map level, &block
    if level == 0
      map(&block)
    else
      map{|e| e.deep_map(level - 1, &block)}
    end
  end
end

p [[1,2], [3,4]].deep_map(1, &:succ)
#=> [[2, 3], [4, 5]]
like image 116
Mladen Jablanović Avatar answered Mar 03 '23 06:03

Mladen Jablanović


Sounds like a job for recursion:

def zipper(args)
  args[0].respond_to?(:each) ? args.map{|a| zipper(a)} : args.map{|i| i+1}
end

zipper([[1,2],[3,4]])
# => [[2, 3], [4, 5]]

zipper([[[1,2],[3,4]],[5,6]])
# => [[[2, 3], [4, 5]], [6, 7]]
like image 32
Abe Voelker Avatar answered Mar 03 '23 06:03

Abe Voelker