Getting a hash from map instead of an array - ruby

a = ["ab", "dc", "vv", "dd"]
a.each_with_index.map { |v, i| { ("prefix_" + i.to_s).to_sym => v }}
=> [{:prefix_0=>"ab"}, {:prefix_1=>"dc"}, {:prefix_2=>"vv"}, {:prefix_3=>"dd"}]
I want to get a hash as a return value. How can I do this?

This is one way :
a = ["ab", "dc", "vv", "dd"]
Hash[a.map.with_index { |v, i| ["prefix_#{i}".to_sym, v] }]
# => {:prefix_0=>"ab", :prefix_1=>"dc", :prefix_2=>"vv", :prefix_3=>"dd"}
In Ruby 2.1 >=, use Array#to_h as below :-
a.map.with_index { |v, i| ["prefix_#{i}".to_sym, v] }.to_h

You can chain enumerators together, so another option is something like this:
a.each_with_index.each_with_object({}) { |(v, i), h| h["prefix_#{i}"] = v }

Related

How do I transpose two hashes in ruby to make a single hash or array?

I want to take the two hashes below and combine them into a new hash or array:
hash1 = {1=>"]", 2=>"}", 3=>")", 4=>"(", 5=>"{", 6=>"["}
hash2 = {1=>"[", 2=>"{", 3=>"(", 4=>")", 5=>"}", 6=>"]"}
I want the result to look something like this:
result = {"["=>"]", "{"=>"}", "("=>")"}
or
result = [ ["[","]"], ["{","}"], ["(",")"] ]
Is there a ruby method that can do this?
You could use Hash#transform_keys:
res = hash1.transform_keys { |k| hash2[k] }
res #=> {"["=>"]", "{"=>"}", "("=>")", ")"=>"(", "}"=>"{", "]"=>"["}
res.first(3) #=> [["[", "]"], ["{", "}"], ["(", ")"]]
Well, another way you can get what you want is by using Hash#deep_merge
like so:
res = hash1.deep_merge(hash2) { |key, this_val, other_val| [other_val , this_val] }.values
# => [["[", "]"], ["{", "}"], ["(", ")"], [")", "("], ["}", "{"], ["]", "["]
res.first(3)
# => [["[", "]"], ["{", "}"], ["(", ")"]]
hash1.each_with_object({}) { |(k, v), h| h[hash2[k]] = v }
#=> {"["=>"]", "{"=>"}", "("=>")", ")"=>"(", "}"=>"{", "]"=>"["}
Or:
hash2.each_with_object({}) { |(k, v), h| h[v] = hash1[k] }
#=> {"["=>"]", "{"=>"}", "("=>")", ")"=>"(", "}"=>"{", "]"=>"["}
hash1.merge(hash2){|_, v1, v2| [v1, v2]}.values
# => [["]", "["], ["}", "{"], [")", "("], ["(", ")"], ["{", "}"], ["[", "]"]]

How to create a hash from a multidimensional array

I have these params :
x=["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]
And when I run this code:
y = x.map { |x| x.match(/^(room)_(adults|childs)_(\d+)/)}
z = y.map { |x| [x[1],[x[2],[x[3].reverse,""]]]}
I get this array:
=> [["room", ["adults", ["1", ""]]], ["room", ["childs", ["1", ""]]], ["room", ["adults", ["2", ""]]], ["room", ["childs", ["2", ""]]]]
I would like to transform this last result into a Hash. If I use z.to_h (collapse last element), I obtain {"room"=>["childs", ["2", ""]]}. But I would like a Hash like this:
{
"room":{
"adults":{[
{"1": ""},
{"2": ""}
]},
"child":{[
{"1": ""},
{"2": ""}
]}
}
}
How can I do?
x.map { |e| e.split '_' }
.group_by(&:shift) # room
.map { |k, v| [k, v.group_by(&:shift) # adults/children
.map { |k, v| [k, v.map { |e, *| {e => ""} } ] }
.to_h ] }
.to_h
#⇒ {"room"=>{"adults"=>[{"1"=>""}, {"2"=>""}],
# "childs"=>[{"1"=>""}, {"2"=>""}]}}
In this case I prefer each_with_object:
x = ["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]
export = x.each_with_object(Hash.new { |k, v| k[v] = Hash.new { |k, v| k[v] = [] } }) do |string, exp|
room, type, id = string.split("_")
exp[room][type] << {id => ""}
end
p export
# => {"room"=>{"adults"=>[{"1"=>""}, {"2"=>""}], "childs"=>[{"1"=>""}, {"2"=>""}]}}
I assume the desired result is
{:room=>{:adults=>[{:"1"=>""}, {:"2"=>""}], :childs=>[{:"1"=>""}, {:"2"=>""}]}}
as the construct given in the question is not a valid Ruby object, let alone a hash.
arr = ["room_adults_1", "room_childs_1", "room_adults_2", "room_childs_2"]
h = arr.map { |s| s.split('_')[1,2].map(&:to_sym) }.group_by(&:first)
#=> {:adults=>[[:adults, :"1"], [:adults, :"2"]],
# :childs=>[[:childs, :"1"], [:childs, :"2"]]}
{ room: h.merge(h) { |k,a,_| a.map { |_,b| { b=>"" } } } }
#=> {:room=>{:adults=>[{:"1"=>""}, {:"2"=>""}], :childs=>[{:"1"=>""}, {:"2"=>""}]}}
If the keys are to be strings, not symbols, remove .map(&:to_sym).

Add values of an array inside a hash

I created a hash that has an array as a value.
{
"0":[0,14,0,14],
"1":[0,14],
"2":[0,11,0,12],
"3":[0,11,0,12],
"4":[0,10,0,13,0,11],
"5":[0,10,0,14,0,0,0,11,12,0],
"6":[0,0,12],
"7":[],
"8":[0,14,0,12],
"9":[0,14,0,0,11,14],
"10":[0,11,0,12],
"11":[0,13,0,14]
}
I want the sum of all values in each array. I expect to have such output as:
{
"0":[24],
"1":[14],
"2":[23],
"3":[23],
"4":[34],
"5":[47],
"6":[12],
"7":[],
"8":[26],
"9":[39],
"10":[23],
"11":[27]
}
I do not know how to proceed from here. Any pointers are thankful.
I would do something like this:
hash = { "0" => [0,14,0,14], "1" => [0,14], "7" => [] }
hash.each { |k, v| hash[k] = Array(v.reduce(:+)) }
# => { "0" => [28], "1" => [14], "7" => [] }
For hash object you as this one.
hash = {"0"=>[0,14,0,14],"1"=>[0,14],"2"=>[0,11,0,12],"3"=>[0,11,0,12],"4"=>[0,10,0,13,0,11],"5"=>[0,10,0,14,0,0,0,11,12,0],"6"=>[0,0,12],"7"=>[],"8"=>[0,14,0,12],"9"=>[0,14,0,0,11,14],"10"=>[0,11,0,12],"11"=>[0,13,0,14]}
You could change value of each k => v pair
hash.each_pair do |k, v|
hash[k] = [v.reduce(0, :+)]
end
will resolve in
hash = {"0"=>[28], "1"=>[14], "2"=>[23], "3"=>[23], "4"=>[34], "5"=>[47], "6"=>[12], "7"=>[0], "8"=>[26], "9"=>[39], "10"=>[23], "11"=>[27]}
If your string is just like you mentioned you can parse it using JSON, or jump that step if you have already an Hash.
You can check the documentation of inject here
require 'json'
json_string = '
{
"0":[0,14,0,14],
"1":[0,14],
"2":[0,11,0,12],
"3":[0,11,0,12],
"4":[0,10,0,13,0,11],
"5":[0,10,0,14,0,0,0,11,12,0],
"6":[0,0,12],
"7":[],
"8":[0,14,0,12],
"9":[0,14,0,0,11,14],
"10":[0,11,0,12],
"11":[0,13,0,14]
}
'
hash = JSON.parse json_string
result = Hash.new
hash.each do |key, value|
result[key] = value.inject(:+)
end
puts result.inspect
and the result:
{"0"=>28, "1"=>14, "2"=>23, "3"=>23, "4"=>34, "5"=>47, "6"=>12, "7"=>nil, "8"=>26, "9"=>39, "10"=>23, "11"=>27}
Classic example for map/reduce. You need to iterate on the hash keys and values (map {|k,v|}), and for each value count the sum using reduce(0) {|acc, x| acc+x}
h = {1 => [1,2,3], 2 => [3,4,5], 7 => []} #example
a = h.map {|k,v| [k ,v.reduce(0) {|acc, x| acc+x}] }
=> [[1, 6], [2, 12], [7, 0]]
Hash[a]
=> {1=>6, 2=>12, 7=>0}

Ruby hash of arrays from array

I have the following array
a=[["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]]
I would like to convert this to a hash that looks like this
{"kvm5"=>["one-417"], "kvm16"=>["one-417", "one-416"]}
everything I have tried squashes the value.
v=Hash[ a.collect do |p| [p[0], [ p[1] ] ] end ]
=> {"kvm5"=>["one-417"], "kvm16"=>["one-416"] }
I was thinking I could check to see if v[p[0]] is an array, but the variable isn't defined inside this block.
Is there a clean way to accomplish what I am looking for?
Yeah, you have to do it yourself, I'm afraid.
a = [["kvm16", "one-415"], ["kvm16", "one-416"], ["kvm5", "one-417"]]
h = a.each_with_object({}) do |(k, v), memo|
(memo[k] ||= []) << v
end
h # => {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}
Or, if you're on older ruby (1.8.x):
h = {}
a.each do |k, v|
(h[k] ||= []) << v
end
h # => {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}
Let's see some functional approaches. This is a common abstraction, you can find it as Enumerable#map_by in Facets:
require 'facets'
hs.map_by { |k, v| [k, v] }
#=> {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}
This pattern replaces the cumbersome group_by + map + Hash:
Hash[hs.group_by(&:first).map { |k, gs| [k, gs.map(&:last)] }]
#=> {"kvm16"=>["one-415", "one-416"], "kvm5"=>["one-417"]}
this has it. in the face of not having each_with_object in my version of ruby and some googleze I arrived at
{}.tap{ |h| items.each{ |item| h[item.id] = item } }
=> {"kvm5"=>["one-417"], "kvm16"=>["one-415", "one-416"]}
mucho thanks to Sergio for getting me thinking along that line

How to convert the values of a hash from String to Array in Ruby?

I'm looking to perform a conversion of the values in a Ruby hash from String to Integer.
I thought this would be fairly similar to the way you perform a conversion in a Ruby array (using the map method), but I have not been able to find an elegant solution that doesn't involve converting the hash to an array, flattening it, etc.
Is there a clean solution to do this?
Eg. From
x = { "a" => "1", "b" => "2", "c"=> "3" }
To
x = { "a" => 1, "b" => 2, "c" => 3 }
To avoid modifying the original Hash (unlike the existing answers), I'd use
newhash = x.reduce({}) do |h, (k, v)|
h[k] = v.to_i and h
end
If you're using Ruby 1.9, you can also use Enumerable#each_with_object to achieve the same effect a bit more cleanly.
newhash = x.each_with_object({}) do |(k, v), h|
h[k] = v.to_i
end
If you want to, you can also extract the logic into a module and extend the Hash class with it.
module HMap
def hmap
self.each_with_object({}) do |(k, v), h|
h[k] = yield(k, v)
end
end
end
class Hash
include HMap
end
Now you can use
newhash = x.hmap { |k, v| v.to_i } # => {"a"=>1, "b"=>2, "c"=>3}
My preferred solution:
Hash[x.map { |k, v| [k, v.to_i]}] #=> {"a"=>1, "b"=>2, "c"=>3}
A somewhat wasteful one (has to iterate over the values twice):
Hash[x.keys.zip(x.values.map(&:to_i))] #=> {"a"=>1, "b"=>2, "c"=>3}
Try this:
x.each{|k,v| x[k]=v.to_i}
p.keys.map { |key| p[key] = p[key].to_i }

Resources