Reputation: 15
I'd like to zip all the array values of a hash. I know there's a way to zip arrays together. I'd like to do that with the values of my hash below.
current_hash = {:a=>["k", "r", "u"],
:b=>["e", " ", "l"],
:c=>["d", "o", "w"],
:d=>["e", "h"]
}
desired_outcome = "keder ohulw"
I have included my desired outcome above.
Upvotes: 0
Views: 335
Reputation: 110725
Here I'm fleshing out @Amadan's remark below the horizontal line in is answer. Suppose:
current_hash = { a:["k","r"], b:["e"," ","l"], c:["d","o","w"], d:["e", "h"] }
and you wished to return "keder ohlw"
. If you made ["k","r"]
and [["e"," ","l"], ["d","o","w"], ["e", "h"]]
zip
's receiver and argument, respectively, you would get "keder oh"
, which omits "l"
and "w"
. (See Array#zip, especially the 3rd paragraph.)
To include those strings you would need to fill out ["k","r"]
with nil
s to make it as long as the longest value, or make zip
's receiver an array of nil
s of the same length. The latter approach can be implemented as follows:
vals = current_hash.values
#=> [["k", "r"], ["e", " ", "l"], ["d", "o", "w"], ["e", "h"]]
([nil]*vals.map(&:size).max).zip(*vals).flatten.compact.join
#=> "keder ohlw"
Note:
a = [nil]*vals.map(&:size).max
#=> [nil, nil, nil]
and
a.zip(*vals)
#=> [[nil, "k", "e", "d", "e"],
# [nil, "r", " ", "o", "h"],
# [nil, nil, "l", "w", nil]]
One could alternatively use Array#transpose rather than zip
.
vals = current_hash.values
idx = (0..vals.map(&:size).max-1).to_a
#=> [0, 1, 2]
vals.map { |a| a.values_at(*idx) }.transpose.flatten.compact.join
#=> "keder ohlw"
See Array#values_at. Note:
a = vals.map { |a| a.values_at(*idx) }
#=> [["k", "r", nil],
# ["e", " ", "l"],
# ["d", "o", "w"],
# ["e", "h", nil]]
a.transpose
#=> [["k", "e", "d", "e"],
# ["r", " ", "o", "h"],
# [nil, "l", "w", nil]]
Upvotes: 0
Reputation: 198436
current_hash.values.then { |first, *rest| first.zip(*rest) }.flatten.compact.join
An unfortunate thing with Ruby zip
is that the first enumerable needs to be the receiver, and the others need to be parameters. Here, I use then
, parameter deconstruction and splat to separate the first enumerable from the rest. flatten
gets rid of the column arrays, compact
gets rid of the nil
(though it's not really necessary as join
will ignore it), and join
turns the array into the string.
Note that Ruby zip
will stop at length of the receiver; so if :a
is shorter than the others, you will likely have a surprising result. If that is a concern, please update with an example that reflects that scenario, and the desired outcome.
Upvotes: 2