I have the following array (SQL result):
[
{:id => 1, :field1 => "one", :field2 => "two"},
{:id => 2, :field1 => "one", :field2 => "two"},
...
]
What I want is:
{
1 => {:field1 => "one", :field2 => "two"},
2 => {:field1 => "one", :field2 => "two"},
...
}
Now I do like the following:
data = {}
result.each do |row|
data[row[:id]] = {:field1 => row[:field1], :field2 => row[:field2]}
end
I'm absolutely sure that's wrong way. What is the best way to do it with Ruby? Is there are any snippet like map or something else?
Hash[arr.map { |h| [h.delete(:id), h] }]
One line :)
hash = arr.clone.each_with_object({}) { |e,res| res[e.delete(:id)] = e }
clone is for not destroying arr variable
Something like this, maybe?
arr = [
{:id => 1, :field1 => "one", :field2 => "two"},
{:id => 2, :field1 => "one", :field2 => "two"}
]
hash = arr.each_with_object({}) do |el, memo|
id = el.delete(:id)
memo[id] = el
end
hash # => {1=>{:field1=>"one", :field2=>"two"}, 2=>{:field1=>"one", :field2=>"two"}}
Related
I have two arrays of hashes.
burgers = [
{:id => 1, :name => "cheese burger"},
{:id => 2, :name => "royale"},
{:id => 3, :name => "big mac"},
{:id => 4, :name => "angus beef"}
]
eaten = [
{:burger_id => 1},
{:burger_id => 2}
]
I would like to return an array or uneaten burgers, where burgers[:id] does not equal eaten[:burger_id]. In burgers_not_eaten_method, I have the expected return value.
def burgers_not_eaten
#Not sure how to compare burger[:id] with eaten[:burger_id]
burgers.reject { |burger| burger[:id] == #eaten burger_id }
# Expected: [{:id => 3, :name => "big mac"},{:id => 4, :name => "angus beef"}]
end
You're close, to make it easy I'd snag all the "eaten" ids into an array, and check for inclusion in that array, like so:
BURGERS = [
{:id => 1, :name => "cheese burger"},
{:id => 2, :name => "royale"},
{:id => 3, :name => "big mac"},
{:id => 4, :name => "angus beef"}
]
EATEN = [
{:burger_id => 1},
{:burger_id => 2}
]
def burgers_not_eaten
eaten_ids = EATEN.map { |e| e[:burger_id] }
BURGERS.reject { |burger| eaten_ids.include?(burger[:id]) }
end
burgers_not_eaten
# => [{:id=>3, :name=>"big mac"}, {:id=>4, :name=>"angus beef"}]
I have an array like:
hasharray=[{"a" => "b" } , {"c" => "d"} , {"e" => "f"}]
I want to create all combinations of this array hash of length min to max.
For instance, with min=0 and max=2, the code should return:
resultarray=[
{},
{"a" => "b" },
{"c" => "d"},
{"e" => "f"},
{"a" => "b" } , {"c" => "d"},
{"c" => "d"} , {"e" => "f"},
{"a" => "b" },{"e" => "f"}
]
How do I do it?
min = 0
max = 2
min.upto(max).flat_map {|n| hasharray.combination(n).to_a }
# => [
# [],
# [{"a"=>"b"}], [{"c"=>"d"}], [{"e"=>"f"}],
# [{"a"=>"b"}, {"c"=>"d"}], [{"a"=>"b"}, {"e"=>"f"}], [{"c"=>"d"}, {"e"=>"f"}]
# ]
I'd like to convert from
{'key1' => (1..10) ,
'key2' => (11..20) ,
'key3' => (21..30)}
to
[{'key1' => 1, 'key2' => 11, 'key3' => 21},
{'key1' => 1, 'key2' => 11, 'key3' => 22},...
.
.
{'key1' => 10, 'key2' => 20, 'key3' => 30}]
How to solve it?
Here it is :
hsh = {'key1' => (1..10) ,'key2' => (11..20) , 'key3' => (21..30)}
keys = hsh.keys
hsh['key1'].to_a.product(hsh['key2'].to_a,hsh['key3'].to_a).map{|a|Hash[keys.zip(a)]}
# => [{'key1' => 1, 'key2' => 11, 'key3' => 21},
# {'key1' => 1, 'key2' => 11, 'key3' => 22},...
# .
# .
# {'key1' => 10, 'key2' => 20, 'key3' => 30}]
You could also write the above as below,when you have more number of keys:
hsh = {'key1' => (1..10) ,'key2' => (11..20) , 'key3' => (21..30)}
keys = hsh.keys
array = hsh.values_at(*keys[1..-1]).map(&:to_a)
hsh['key1'].to_a.product(*array).map{|a|Hash[keys.zip(a)]}
So many ways... A kiss answer (edited to extend to any number of keys):
s = {'key1' => (1..10), 'key2' => (11..20), 'key3' => (21..30)}
r = []
s.each {|k,v| a = []; (v.to_a).each {|i| a << {k=>i}}; r << a}
result = r.shift
r.each {|e| result = result.product(e).map(&:flatten)}
result
h = {
'key1' => (1..10),
'key2' => (11..20),
'key3' => (21..30)
}
h.map { |k,v| [k].product(v.to_a) }.transpose.map { |e| Hash[e] }
#=> [{"key1"=>1, "key2"=>11, "key3"=>21},
# {"key1"=>2, "key2"=>12, "key3"=>22},
# {"key1"=>3, "key2"=>13, "key3"=>23},
# {"key1"=>4, "key2"=>14, "key3"=>24},
# {"key1"=>5, "key2"=>15, "key3"=>25},
# {"key1"=>6, "key2"=>16, "key3"=>26},
# {"key1"=>7, "key2"=>17, "key3"=>27},
# {"key1"=>8, "key2"=>18, "key3"=>28},
# {"key1"=>9, "key2"=>19, "key3"=>29},
# {"key1"=>10, "key2"=>20, "key3"=>30}]
h = {'key1' => (1..10) ,
'key2' => (11..20) ,
'key3' => (21..30)}
arrays = h.values.map(&:to_a).transpose
p arrays.map{|ar| Hash[h.keys.zip(ar)] }
#=> [{"key1"=>1, "key2"=>11, "key3"=>21}, {"key1"=>2, "key2"=>12, "key3"=>22},...
h = {'key1' => (1..10), 'key2' => (11..20), 'key3' => (21..30)}
Edit 1: made some changes, principally the use of inject({}):
f,*r = h.map {|k,v| [k].product(v.to_a)}
f.zip(*r).map {|e| e.inject({}) {|h,a| h[a.first] = a.last; h}}
Edit 2: After seeing the use of Hash[] in #Phrogz's answer to another question:
f,*r = h.map {|k,v| [k].product(v.to_a)}
f.zip(*r).map {|e| Hash[*e.flatten]}
Lazier way of doing the same:
h = {
'key1' => (1..10),
'key2' => (11..20),
'key3' => (21..30)
}
result = ( 0...h.values.map( &:to_a ).map( &:size ).max ).map do |i|
Hash.new { |hsh, k| hsh[k] = h[k].to_a[i] }
end
result[1]['key3'] #=> 22
I need to find a certain hash element where one of the keys is equal to a certain value. I've tried many ways and can't seem to figure it out with jsonpath gem.
Need to get tire tag where grip == 'bad'
require "jsonpath"
hash = {
:id => 1,
:cars => [
{:id => 1, :tire => {:grip => "good", :color => "black"}},
{:id => 2, :tire => {:grip => "bad", :color => "red"}},
{:id => 3, :tire => {:grip => "good", :color => "green"}}
]
}
puts JsonPath.on(hash, "$..tire[?(#['grip'] == 'bad')]").inspect
No results.
The [?()] filter only works for arrays (or at least for either arrays or hashes, not both at the same time). In order for it to work, I had to enclose the :tire hash in an array.
Original:
:tire => {:grip => "good", :color => "black"}
New:
:tire => [{:grip => "good", :color => "black"}]
That's a "fix" that works for me. It would be better if someone fixed the jsonpath gem to make it work for both arrays and hashes (of the same type and at the same time).
In JsonPath.on first argument must be json, not hash.
I can't do it over ruby, it is not fully correct solution. But may be this help you.
require 'jsonpath'
require 'json'
hash = {
:id => 1,
:cars => [
{:id => 1, :tire => {:grip => "good", :color => "black"}},
{:id => 2, :tire => {:grip => "bad", :color => "red"}},
{:id => 3, :tire => {:grip => "good", :color => "green"}}
]
}
json = hash.to_json
obj = JsonPath.new( "$..tire")[json]
result = obj.inject(Array.new){|res, x| res << x if x["grip"]=='bad'; res }
p result # [{"grip"=>"bad", "color"=>"red"}]
I'm trying this:
{:id => 5, :foos => [1,2,3]}.each {|k,v| v.to_s}
But that's returning this:
{:id=>5, :foos=>[1, 2, 3]}
I'd like to see this:
{:id=>"5", :foos=>"[1, 2, 3]"}
I've also tried variations of Hash#collect and Hash#map. Any ideas?
you could use Object#inspect:
{ :id => 5, :foos => [1, 2, 3] }.inject({}) do |hash, (key, value)|
hash.merge key => value.inspect
end
which returns:
{ :foos => "[1, 2, 3]", :id => "5" }
or if you want it to be destructive:
hash = { :id => 5, :foos => [1, 2, 3] }
hash.each_key { |key| hash[key] = hash[key].inspect }
Your stuff doesn't work because v.to_s doesn't modify v, so essentially the block doesn't do anything.
You could do it like this:
hash = {:id => 5, :foos => [1,2,3]}
hash.each_key { |k| hash[k] = hash[k].to_s }
If you don't want to modify the hash:
hash = {:id => 5, :foos => [1,2,3]}
new_hash = {}
hash.each_key { |k| new_hash[k] = hash[k].to_s }