Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

simple hash merge by array of keys and values in ruby (with perl example)

In Perl to perform a hash update based on arrays of keys and values I can do something like:

@hash{'key1','key2','key3'} = ('val1','val2','val3');

In Ruby I could do something similar in a more complicated way:

hash.merge!(Hash[ *[['key1','key2','key3'],['val1','val2','val3']].transpose ])

OK but I doubt the effectivity of such procedure.

Now I would like to do a more complex assignment in a single line.

Perl example:

(@hash{'key1','key2','key3'}, $key4) = &some_function();

I have no idea if such a thing is possible in some simple Ruby way. Any hints?

For the Perl impaired, @hash{'key1','key2','key3'} = ('a', 'b', 'c') is a hash slice and is a shorthand for something like this:

$hash{'key1'} = 'a';
$hash{'key2'} = 'b';
$hash{'key3'} = 'c';
like image 266
geronime Avatar asked Aug 16 '11 20:08

geronime


People also ask

How do you merge an Array of hashes in Ruby?

Method 2: Use the inject method Hash#merge creates a new hash on each iteration. For bigger arrays, it is more performant to use Hash#merge! / Hash#update .

How do I merge a Hash in Perl?

To combine two hashes, look at them as lists and assign them to a hash. my %new_hash = (%hash1, %hash2); The right-hand side of the equals is a long list of key/value pairs from both of the hashes.

What is Array of hashes in Ruby?

A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.


2 Answers

In Ruby 1.9 Hash.[] can take as its argument an array of two-valued arrays (in addition to the old behavior of a flat list of alternative key/value arguments). So it's relatively simple to do:

mash.merge!( Hash[ keys.zip(values) ] )

I do not know perl, so I'm not sure what your final "more complex assignment" is trying to do. Can you explain in words—or with the sample input and output—what you are trying to achieve?

Edit: based on the discussion in @fl00r's answer, you can do this:

def f(n)
  # return n arguments
  (1..n).to_a
end

h = {}
keys = [:a,:b,:c]
*vals, last = f(4)
h.merge!( Hash[ keys.zip(vals) ] )
p vals, last, h
#=> [1, 2, 3]
#=> 4
#=> {:a=>1, :b=>2, :c=>3}

The code *a, b = some_array will assign the last element to b and create a as an array of the other values. This syntax requires Ruby 1.9. If you require 1.8 compatibility, you can do:

vals = f(4)
last = vals.pop
h.merge!( Hash[ *keys.zip(vals).flatten ] )
like image 60
Phrogz Avatar answered Nov 14 '22 20:11

Phrogz


You could redefine []= to support this:

class Hash
  def []=(*args)
    *keys, vals = args # if this doesn't work in your version of ruby, use "keys, vals = args[0...-1], args.last"
    merge! Hash[keys.zip(vals.respond_to?(:each) ? vals : [vals])]
  end
end

Now use

myhash[:key1, :key2, :key3] = :val1, :val2, :val3
# or
myhash[:key1, :key2, :key3] = some_method_returning_three_values
# or even
*myhash[:key1, :key2, :key3], local_var = some_method_returning_four_values
like image 24
jtbandes Avatar answered Nov 14 '22 20:11

jtbandes