Reputation: 15284
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 :)
Upvotes: 4
Views: 1940
Reputation: 66837
Personally I just would write it like one of the two options below and be done with it:
arr.map { |a| a.map(&:next) }
#=> [[2, 3], [4, 5]]
arr.map { |x, y| [x.next, y.next] }
#=> [[2, 3], [4, 5]]
Upvotes: 0
Reputation: 44080
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]]
Upvotes: 3
Reputation: 31574
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]]
Upvotes: 3
Reputation: 168081
In order to do that by writing x+1
only once, you need to put it in a block. Otherwise, you can do:
new_arr = arr.map{|x, y| [x+1, y+1]}
Or, if you insist, you can do:
new_arr = arr.flatten(1).map{|x| x+1}.each_slice(2).to_a
Upvotes: 0