Hi,
I want to convert an array (with pairs [key,value]) to a hash. After
googling a bit I got:
- Hash[*array.flatten]
But this is not safe as Array#flatten is recursive.
- array.inject({}) { |m, e| m[e[0]] = e[1]; m }
Too ugly to consider.
- Hash[*array.flatten(1)]
That’s my own attempt. It only works for Ruby > 1.9.
But I am still wondering why…
- Array objects have no “to_h” method?
- Hasy[*array] don’t accept the [key,value] pairs (as Hash#to_a calls
return)?
thanks,
holden
You could open up the Array class and add a to_hash method that uses the
ugly inject idiom to make it more convenient.
class Array
def to_hash
inject({}) { |m, e| m[e[0]] = e[1]; m }
end
end
On Jan 9, 3:31 pm, Holden H. removed_email_address@domain.invalid wrote:
return)?
These would only work in one specific case. What would this do?
[1, 2, 3].to_h
I just did this myself. Because I knew how the data was stored in the
array, I was able to control how I put it into the hash. What I did was
this:
myArray=[‘255’,‘10’,‘258’,‘08’,‘154’,‘34’] #(etc)
myHash={}
c=0
while c<=myArray.size
myHash.store(myArray[c],myArray[c+1])
c+=2
end
I don’t suggest that this absolutely the best way to do it, and it may
not work if you don’t know your data structure in the array but it was a
quick and easy way for me to solve my problem.
2008/1/9, Holden H. removed_email_address@domain.invalid:
I want to convert an array (with pairs [key,value]) to a hash. After
googling a bit I got:
- Hash[*array.flatten]
But this is not safe as Array#flatten is recursive.
- array.inject({}) { |m, e| m[e[0]] = e[1]; m }
Too ugly to consider.
You can do this:
array.inject({}) {|ha, (k, v)| ha[k] = v; ha}
Is this nicer according to your standards?
Kind regards
robert
I want to convert an array (with pairs [key,value]) to a hash. After
googling a bit I got:
- Hash[*array.flatten]
But this is not safe as Array#flatten is recursive.
- array.inject({}) { |m, e| m[e[0]] = e[1]; m }
Too ugly to consider.
array.inject({}) {|h,(k,v)| h[k]=v; h}
is still ugly, but perhaps you may consider it.
Also, arrays of arrays can be used directly in a hash-like manner:
array = [[1,2],[3,4]]
array.assoc(3)
=> [3,4]
which may be useful, depending on what you’re doing (it won’t have hash
performance, but it may do)
Dan.
yermej wrote:
On Jan 9, 3:31�pm, Holden H. removed_email_address@domain.invalid wrote:
return)?
These would only work in one specific case. What would this do?
[1, 2, 3].to_h
That’s why I prefer [key, value] pairs, to avoid any ambiguity.
However, a theorical [1,2,3].to_h should give the same error than
Hash[*array] gives:
irb(main):002:0> Hash[*[1,2,3]]
ArgumentError: odd number of arguments for Hash
2008/1/10, Holden H. removed_email_address@domain.invalid:
However, a theorical [1,2,3].to_h should give the same error than
Hash[*array] gives:
irb(main):002:0> Hash[*[1,2,3]]
ArgumentError: odd number of arguments for Hash
There is however one issue with a general Array to Hash conversion
routine: there are different ways to convert a Hash and to me it’s not
clear which one should be the default. Options I can see so far:
-
if array contains pairs use them as key and value for the Hash
-
if array is flat use consecutive entries for key and value (fails
with odd number of entries)
-
use indexes as keys and entries as values, e.g.
ar.to_enum(:each_with_index).inject({}) {|h, (v,k)| h[k]=v;h}
The last one has not been mentioned so far but there might actually be
uses for this (i.e. when one wants to replace an Array with a Hash).
Dunno how often this might occur though.
There are probably other valid default conversion candidates, too.
Kind regards
robert
Robert K. wrote:
You can do this:
array.inject({}) {|ha, (k, v)| ha[k] = v; ha}
Is this nicer according to your standards?
Thanks. Yes, it’s clearer to separate key/value on the args. I must
clarify that by “too ugly to consider” I meant “I imagine there is a
nicer way to do it”.
Inject solutions are fine, short and clear, but I am still puzzled why
Hash[*array] and Hash#to_a are not specular (maybe this is a question
for a ruby-dev forum, though)
regards
Holden H. wrote:
yermej wrote:
On Jan 9, 3:31�pm, Holden H. removed_email_address@domain.invalid wrote:
return)?
These would only work in one specific case. What would this do?
[1, 2, 3].to_h
That’s why I prefer [key, value] pairs, to avoid any ambiguity.
However, a theorical [1,2,3].to_h should give the same error than
Hash[*array] gives:
irb(main):002:0> Hash[*[1,2,3]]
ArgumentError: odd number of arguments for Hash
I disagree. Array#to_hash should work so that hash.to_a.to_hash == hash,
in other words it should create a hash only if it contains key value
tuples.
array.inject({}) {|ha, (k, v)| ha[k] = v; ha}
There’s absolutely no advantage using inject like this.
h={}; array.each { |k,v| h[k]=v }
Is less code and faster.
ar.to_enum(:each_with_index).inject({}) {|h, (v,k)| h[k]=v;h}
Same here
h={}; array.each_with_index { |k,v| h[k]=v }
Personally I use the following code in my lib:
create a hash from an array with keys and an array with values
you can set default or provide a block just as with Hash::new
def Hash.zip(keys, values, default=nil, &block)
hash = block_given? ? Hash.new(&block) : Hash.new(default)
keys.zip(values) { |k,v| hash[k]=v }
hash
end
class Array
create a hash from an array of [key,value] tuples
you can set default or provide a block just as with Hash::new
Note: if you use [key, value1, value2, value#], hash[key] will
be [value1, value2, value#]
def to_hash(default=nil, &block)
hash = block_given? ? Hash.new(&block) : Hash.new(default)
each { |(key, *value)| hash[key]=*value }
hash
end
end
Regards
Stefan
I don’t think I saw this one:
h = {}; [1,2,3,4].each_slice(2) {|k,v| h[k]=v}
Array objects have no “to_h” method?
they do
[[:one, 1], [:two, 2]].to_h
=> {:one=>1, :two=>2}
[:one, 1, :two, 2, :buckle_my, “shoe”, :three].each_slice(2).map {|a,b|
[a,b]}.to_h
=> {:one=>1, :two=>2, :buckle_my=>“shoe”, :three=>nil}
This works because each_slice returns an enumerator. cool.