Forum: Ruby Help constructing interesting hash?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
christopher.mcmahon (Guest)
on 2005-11-30 23:59
(Received via mailing list)
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
rosco (Guest)
on 2005-12-01 00:24
(Received via mailing list)
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"]}
gwtmp01 (Guest)
on 2005-12-01 00:28
(Received via mailing list)
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}"
mental (Guest)
on 2005-12-01 00:32
(Received via mailing list)
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
Sean O. (Guest)
on 2005-12-01 00:48
(Received via mailing list)
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
belorion (Guest)
on 2005-12-01 01:08
(Received via mailing list)
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"]}
Sean O. (Guest)
on 2005-12-01 01:16
(Received via mailing list)
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 :)

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
dblack (Guest)
on 2005-12-01 02:05
(Received via mailing list)
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
lazax.com (Guest)
on 2005-12-01 03:09
(Received via mailing list)
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
martindemello (Guest)
on 2005-12-01 15:14
(Received via mailing list)
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
dave (Guest)
on 2005-12-01 23:59
(Received via mailing list)
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
neoneye (Guest)
on 2005-12-02 01:12
(Received via mailing list)
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"}
This topic is locked and can not be replied to.