2

Having two arrays of different sizes, I'd like to get the longer array as keys and the shorter one as values. However, I don't want any keys to remain empty, so that is why I need to keep iterating on the shorter array until all keys have a value.

EDIT: I want to keep array longer intact, but without empty values, that means keep iterating on shorter until all keys have a value.

longer  = [1, 2, 3, 4, 5, 6, 7]
shorter = ['a', 'b', 'c']

Hash[longer.zip(shorter)]
#=> {1=>"a", 2=>"b", 3=>"c", 4=>nil, 5=>nil, 6=>nil, 7=>nil}

Expected Result

#=> {1=>"a", 2=>"b", 3=>"c", 4=>"a", 5=>"b", 6=>"c", 7=>"a"}
Leo
  • 129
  • 9

3 Answers3

7

Here's an elegant one. You can "loop" the short array

longer  = [1, 2, 3, 4, 5, 6, 7]
shorter = ['a', 'b', 'c']

longer.zip(shorter.cycle).to_h # => {1=>"a", 2=>"b", 3=>"c", 4=>"a", 5=>"b", 6=>"c", 7=>"a"}
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
1

A crude way until you find something more elegant: Slice the longer array as per length of shorter one, and iterate over it to re-map the values.

mapped = longer.each_slice(shorter.length).to_a.map do |slice|
           Hash[slice.zip(shorter)]
         end
=> [{1=>"a", 2=>"b", 3=>"c"}, {4=>"a", 5=>"b", 6=>"c"}, {7=>"a"}]

Merge all hashes withing the mapped array into a single hash

final = mapped.reduce Hash.new, :merge
=> {1=>"a", 2=>"b", 3=>"c", 4=>"a", 5=>"b", 6=>"c", 7=>"a"}
Sam
  • 884
  • 6
  • 15
0

Here's a fun answer.

longer  = [1, 2, 3, 4, 5, 6, 7]
shorter = ['a', 'b', 'c']

h = Hash.new do |h,k|
  idx = longer.index(k)
  idx ? shorter[idx % shorter.size] : nil
end
  #=> {}

h[1] #=> a
h[2] #=> b
h[3] #=> c
h[4] #=> a
h[5] #=> b
h[6] #=> c
h[7] #=> a
h[8] #=> nil

h #=> {}

h.values_at(3,5) #=> ["c", "b"] 

If this is not good enough (e.g., if you wish to use Hash methods such as keys, key?, merge, to_a and so on), you could create the associated hash quite easily:

longer.each { |n| h[n] = h[n] }
h #=> {1=>"a", 2=>"b", 3=>"c", 4=>"a", 5=>"b", 6=>"c", 7=>"a"}  
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100