Help constructing interesting hash?


#1

Suppose I have an array of arrays like

[[A, B, C][1,2,3]]

I can easily make a hash (using each_with_index) where one value is
the lookup value for the other value:

1=>A
2=>B
3=>C

Now suppose I have a AoA like

[[A, B, C,][1,2,2]]

Is there a readable way to construct a hash like

1=>[A]
2=>[B, C]

?

Thanks,
-Chris


#2

On Wed, 30 Nov 2005 21:57:00 -0000, Chris McMahon
removed_email_address@domain.invalid wrote:

Thanks,
-Chris

I assumed you meant [[‘A’, ‘B’, ‘C’],[1,2,2]] - I know comma at end can
be
valid syntax but not sure if it fits here?
If so, this seems to work:

a = [[‘A’, ‘B’, ‘C’],[1,2,2]]

h = {}
a[1].each_with_index { |it, i|
(h[it] ||= []) << a[0][i]
}

p h # => {1=>[“A”], 2=>[“B”, “C”]}


#3

On Nov 30, 2005, at 4:57 PM, Chris McMahon wrote:

Now suppose I have a AoA like

[[A, B, C,][1,2,2]]

Is there a readable way to construct a hash like

1=>[A]
2=>[B, C]

input = [[:A, :B, :C],[1,2,2]]

output = {}
input[1].each_with_index { |group, index| (output[group] ||= []) <<
input[0][index] }

puts “input: #{input.inspect}”
puts “output: #{output.inspect}”


#4

On 11/30/05, Chris McMahon removed_email_address@domain.invalid wrote:

Thanks,
-Chris

Here’s another way:

a = [[‘A’, ‘B’, ‘C’],[1,2,3]]
h = Hash[*a[0].zip(a[1]).flatten]
p h
– OUTPUT –
{“A”=>1, “B”=>2, “C”=>3}

Regards,

Sean


#5

Quoting Chris McMahon removed_email_address@domain.invalid:

3=>C
def build( values, keys )
hash = {}
values.zip( keys ) do |value, key|
hash[key] = value
end
hash
end

build(*[[A, B, C], [1, 2, 3]]) #=> {1=>A, 2=>B, 3=>C}

Now suppose I have a AoA like

[[A, B, C,][1,2,2]]

Is there a readable way to construct a hash like

1=>[A]
2=>[B, C]

def build2( values, keys )
hash = {}
values.zip( keys ) do |value, key|
( hash[key] ||= [] ).push value
end
hash
end

build2(*[[A, B, C], [1, 2, 2]]) #=> {1=>[A], 2=>[B, C]}

In other words, you can probably just take the code you’ve got and
replace:

hash[key] = value

with:

( hash[key] ||= [] ).push value

This bit can also be written in longer form as:

hash[key] ||= []
hash[key].push value

or even:

hash[key] = [] unless hash[key]
hash[key].push value

(but the shortest form saves you one or two hash lookups)

-mental


#6

Oops - I didn’t read the full message properly.

Here really is a way to achieve what you’re after (though whether it
is readable or not is moot :slight_smile:

a = [[‘A’, ‘B’, ‘C’],[1,2,2]]
h = a[1].zip(a[0]).inject(Hash.new{|h, k| h[k] = Array.new}) {|h, k|
h[k[0]] << k[1]; h }
p h
– OUTPUT –
{1=>[“A”], 2=>[“B”, “C”]}

Regards,

Sean


#7

Hi –

On Thu, 1 Dec 2005, Chris McMahon wrote:

Now suppose I have a AoA like

[[A, B, C,][1,2,2]]

Is there a readable way to construct a hash like

1=>[A]
2=>[B, C]

Here’s an inject-based way:

a.zip(b).inject({}) do |hash,(key,value)|
(hash[value] ||= []) << key
hash
end

David


#8

Firstly, you can use Array#zip instead of each_with_index:

h = {}
[1,2,3].zip([:a, :b, :c]){|key, value| h[key] = value }

h
=> {1=>:a, 2=>:b, 3=>:c}

Secondly, here is the answer

h = {}
[1, 2, 2].zip([:a, :b, :c]){|key, value|
h[key] ||= []; # if key not defined, make it []
h[key]<<value
}

h
=> {1=>[:a], 2=>[:b, :c]}

You can also combine the two lines in
(h[key] ||= []) << value


#9

Here’s a construct I use quite frequently:

hsh = Hash.new{ |hh,kk| hh[kk] = Array.new }

What this does is that the default value of hsh[somekey] is a new array.

So, with that, we can do this:

a = [[‘a’, ‘b’, ‘c’],[1,2,2]]
hsh = Hash.new{ |hh,kk| hh[kk] = Array.new }
a[0].each_index{ |ii|
hsh[ a[1][ii] ].push( a[0][ii] )
}
hsh => {1=>[“a”], 2=>[“b”, “c”]}


#10

David A. Black wrote:

a.zip(b).inject({}) do |hash,(key,value)|
(hash[value] ||= []) << key
hash
end

or, using Hash’s default initializer:

hash = Hash.new {|h, k| h[k] = [] }
a.zip(b).inject(hash) do |h, (k, v)|
h << k
end

Cheers,
Dave


#11

On 11/30/05, Chris McMahon removed_email_address@domain.invalid wrote:
[snip]

I can easily make a hash (using each_with_index) where one value is
the lookup value for the other value:

#invert can sometimes be useful… but not in this case

Hash[*%w(a b c).zip([1, 2, 2]).flatten].invert
#=> {1=>“a”, 2=>“c”}


#12

removed_email_address@domain.invalid wrote:

def build2( values, keys )
hash = {}
values.zip( keys ) do |value, key|
( hash[key] ||= [] ).push value
end
hash
end

build2(*[[A, B, C], [1, 2, 2]]) #=> {1=>[A], 2=>[B, C]}

The other interesting way is

def build3(values, keys)
hash = Hash.new {|h,k| h[k] = []}
values.zip(keys) do |value, key|
hash[key].push value
end
hash
end

i.e. pushing the responsibility of generating a [] the first time a key
is seen off onto the hash rather than the insertion code.

martin