-1

I have these arrays:

positions = [[0, 1, 2], [2, 3]] 
values = [[15, 15, 15], [7, 7]]
keys = [1, 4]

I need to create a hash whose keys are from keys and the values are from values. Values must be at indices defined in positions. If no index is defined,nil` should be added to that index.

The three arrays contain the same number of elements; keys has two elements, values two, and positions two. So it's ok.

Expected output:

hash = {1=>[15, 15, 15, nil], 4=>[nil, nil, 7, 7]}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Julius Dzidzevičius
  • 10,775
  • 11
  • 36
  • 81

4 Answers4

2

Let the zippery begin (answer to the original question):

row_size = positions.flatten.max.next

rows = positions.zip(values).map do |row_positions, row_values|
  row = Array.new(row_size)
  row_positions.zip(row_values).each_with_object(row) do |(position, value), row|
    row[position] = value
  end
end

keys.zip(rows).to_h # => {1=>[15, 15, 15, nil], 4=>[nil, nil, 7, 7]}
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • 1
    As a public service, please consider adding a sentence at the beginning that states the question you are answering. :-) – Cary Swoveland Apr 05 '17 at 07:17
  • Any reason you use `.next` instead of ` + 1`? – Eric Duminil Apr 05 '17 at 07:45
  • 1
    @EricDuminil, generally, I use `+ 1` only if I was already writing some formula `x * y - z + 1`. It's a bit weird mixing English explanations (aka method calls/chains) with math notations (aka operations `+` and numbers `1`) and vice versa. But it's a minor thingy and very much opinion based. – ndnenkov Apr 05 '17 at 07:51
  • Okay. It might cause confusion with `Enumerator#next` though. ` + 1` is less ambiguous IMHO. – Eric Duminil Apr 05 '17 at 07:57
  • “It's a bit weird mixing English explanations (aka method calls/chains) with math notations”—use `positions.flatten.max.public_send(:+, 1)` :) – Aleksei Matiushkin Apr 05 '17 at 08:00
  • @EricDuminil, there is also `String#next`. The idea is that there are many *?nextable?* things in Ruby, just like there are many things you can add `1` to. You have to be aware what is the concept behind the thing you are *?nexting?*. But if you prefer, you can use `#succ` instead. – ndnenkov Apr 05 '17 at 08:02
  • to me `next` here is perfectly Rubyish :) – Andrey Deineko Apr 05 '17 at 08:06
  • An alternative would be `row_size = positions.flatten.max + 1 # Believe me, I'm a nerd!` ;) – Eric Duminil Apr 05 '17 at 08:09
  • @EricDuminil, apparently using `#next` is [57 / 11 times](http://stackoverflow.com/a/17523916/2423164) better than + 1. (: – ndnenkov Apr 05 '17 at 12:19
  • @ndn: Thanks for the laugh. It's also -57/31 better than the accepted answer. – Eric Duminil Apr 05 '17 at 12:32
1

Not the cleanest.. but works :P

max = positions.flatten.max + 1
pv = positions.zip(values).map { |o| o.transpose.to_h }
h = {}
pv.each_with_index do |v, idx|
  h[keys[idx]] = Array.new(max).map.with_index { |_, i| v[i] }
end

# h
# {1=>[15, 15, 15, nil], 4=>[nil, nil, 7, 7]}

or if you prefer a more compressed but less readable one..

keys.zip(positions.zip(values).map { |o| o.transpose.to_h }).reduce({}) do |h, (k, v)|
  h[k] = Array.new(max).map.with_index { |_, i| v[i] }
  h
end
Ho Man
  • 2,308
  • 11
  • 16
  • A number of elements is not strictly limited to `4`. – Aleksei Matiushkin Apr 05 '17 at 07:41
  • That was not mentioned in the original question, but it's fixed now. Just need to take the highest index in positions. And actually, in one of the comments, the author also mentioned the highest index IS 3. @mudasobwa – Ho Man Apr 05 '17 at 07:44
  • Highest index IS `3` ≠ a number of elements is strictly limited to `4`. BTW, using such constants hardcoded is a code smell. – Aleksei Matiushkin Apr 05 '17 at 07:58
  • It doesn't matter how many elements there are in this scenario though. And I absolutely think it's fine if it was extracted as a constant and if it is a recognized and accepted constraint of the system, which also removes the need to re-calculate the max every time. – Ho Man Apr 05 '17 at 08:04
1

Just out of curiosity:

nils = (0..positions.flatten.max).zip([nil]).to_h
keys.zip(positions, values).group_by(&:shift).map do |k, v|
  [k, nils.merge(v.shift.reduce(&:zip).to_h).values]
end.to_h
#⇒ {1=>[15, 15, 15, nil], 4=>[nil, nil, 7, 7]}
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
0
new_hash = {}

keys.each_with_index do |key, index|

    new_hash[key] = Array.new(positions.flatten.max + 1)
    value_array = values[index] 
    position_array = positions[index] 
    position_array.each_with_index.map { |element, i| new_hash[key][element] = value_array[i]} 
end 
new_hash

I hope this will work.