I have a hash:
a = { 21 => { 3 => {:x => 5, :y => 6}}}
I want to add another value to the key '21' so that the hash looks like this:
a = { 21 => { 3 => {:x => 5, :y => 6}, 4 => {:x => 8, :y => 7}}}
How can I do that?
You want to add an key-value pair to a hash (a[21]). a[21] will give you the inner hash object.
a = { 21 => { 3 => {:x => 5, :y => 6}}}
a[21]
# => {3=>{:x=>5, :y=>6}}
Associating key, values to the inner hash will solve your problem.
a[21][4] = {:x => 8, :y => 7}
a
# => {21=>{3=>{:x=>5, :y=>6}, 4=>{:x=>8, :y=>7}}}
Another way is:
a[21].update({ 4=>{:x => 8, :y => 7} })
a #=> {21=>{3=>{:x=>5, :y=>6}, 4=>{:x=>8, :y=>7}}}
Related
I have an array:
[
{a => 1, b => { c => 1, d => 1}},
{a => 1, b => { c => 1, d => 2}},
{a => 1, b => { c => 2, d => 2}},
{a => 2, b => { c => 1, d => 1}},
]
I want to change it to this:
[
{a => 1, b => [{ c => 1, d => [1, 2]}, { c => 2, d => [2]}]},
{a => 2, b=> [ { c=> 1, d => [1] } ]}
]
Rules/Requirements:
Hashes of same value of a go to one hash
b should be an array of {c => , d =>}
d should be an array
d with same value of c go to same array
Here is a solution. It is very explicit, so it would not generalize to other hash structures.
hashes = [
{:a => 1, :b => { :c => 1, :d => 1}},
{:a => 1, :b => { :c => 1, :d => 2}},
{:a => 1, :b => { :c => 2, :d => 2}},
{:a => 2, :b => { :c => 1, :d => 1}},
]
a_values = {}
hashes.each do |hash|
a_value = hash[:a]
a_values[a_value] ||= {}
c_value = hash[:b][:c]
a_values[a_value][c_value] ||= { :c => c_value, :d => [] }
d_value = hash[:b][:d]
a_values[a_value][c_value][:d].push(d_value)
end
# Now aggregate the results
results = a_values.map do |a_value, c_hashes|
b_arr = c_hashes.map { |c_value, c_hash| c_hash }
{ :a => a_value, :b => b_arr }
end
And here is the output:
[
{:a=>1, :b=>[{:c=>1, :d=>[1, 2]}, {:c=>2, :d=>[2]}]},
{:a=>2, :b=>[{:c=>1, :d=>[1]}]}
]
I have a data structure in the following format:
data_hash = [
{ price: 1, count: 3 },
{ price: 2, count: 3 },
{ price: 3, count: 3 }
]
Is there an efficient way to get the values of :price as an array like [1,2,3]?
First, if you are using ruby < 1.9:
array = [
{:price => 1, :count => 3},
{:price => 2, :count => 3},
{:price => 3, :count => 3}
]
Then to get what you need:
array.map{|x| x[:price]}
There is a closed question that redirects here asking about handing map a Symbol to derive a key. This can be done using an Enumerable as a middle-man:
array = [
{:price => 1, :count => 3},
{:price => 2, :count => 3},
{:price => 3, :count => 3}
]
array.each.with_object(:price).map(&:[])
#=> [1, 2, 3]
Beyond being slightly more verbose and more difficult to understand, it also slower.
Benchmark.bm do |b|
b.report { 10000.times { array.map{|x| x[:price] } } }
b.report { 10000.times { array.each.with_object(:price).map(&:[]) } }
end
# user system total real
# 0.004816 0.000005 0.004821 ( 0.004816)
# 0.015723 0.000606 0.016329 ( 0.016334)
The input hash can have nests of any combo of Arrays and Hashes (AoA, AoH, HoH, and HoA). Flatting the hash elements to have the proper key and delimiter of _> is no problem.
However, I'm having trouble when an Array comes into the picture and I need to grab each element and stick it to the proper key while continuing to build the output. The final output should be a 1-D array of hashes with the only difference being the each array elements.
For example:
if the input hash is:
{:x => 333, :y => 13, :z => [1,2,{:zz => [40,50]},[10,20]], :a => {:o => "1", :p => "2"}}
The final result should be:
`[{:x => 333, :y => 13, :z => 1, :z_>zz => 40, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 1, :z_>zz => 50, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 2, :z_>zz => 40, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 2, :z_>zz => 50, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 10, :z_>zz => 40, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 10, :z_>zz => 50, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 20, :z_>zz => 40, :a_>o => 1, a_>p => 2},
{:x => 333, :y => 13, :z => 20, :z_>zz => 50, :a_>o => 1, a_>p => 2}]`
This is long and complicated, but at least it works:
my_hash = {:x => 333, :y => 13, :z => [1,2,{:zz => [40,50]},[10,20]], :a => {:o => "1", :p => "2"}}
# Create Recursive function to get values:
def advance_hash_flattener(input, parent=[])
case input
when Hash then input.flat_map{|key, val|
advance_hash_flattener(val, parent+[key])}
when Array then input.flat_map{|x| advance_hash_flattener(x, parent)}
else [parent.join('_>'), input]
end
end
#Some small transformations for the last step:
first_step = advance_hash_flattener(my_hash)
.each_slice(2)
.group_by{|x| x.first}
.map{|x| [x.first, x.last.map(&:last)]}
p first_step #=> [["x", [333]], ["y", [13]], ["z", [1, 2, 10, 20]], ["z_>zz", [40, 50]], ["a_>o", ["1"]], ["a_>p", ["2"]]]
# Create an array of Hashes:
final_array = [Hash.new]
first_step.each do |key,values|
new = []
values.each do |val|
if final_array.first.key?(key)
final_copy = final_array.map{|x|x.clone}
final_copy.each{|x| x[key] = val}
new += final_copy
else
final_array.each{|x| x[key] = val}
end
end
final_array += new
end
# result stored in final_array
Given a hash
z = [{'a' => 1, 'b' => 2}, {'a' => 3, 'b' => 4}, {'a' => 1, 'b' => 4}]
How do I search if the search parameter itself is a hash e.g.
{'a' => 3}
so that I can do something like z.find_by_hash({'a' => 3}) for it to return
{'a' => 3, 'b' => 4}
and also to get a collection of arrays like z.find_by_hash({'a' => 1}) for it to return
[{'a' => 1, 'b' => 2}, {'a' => 1, 'b => 4}]
Thanks
You can do this:
class Array
def find_by_hash(hash)
self.select { |h| h.includes_hash?(hash) }
end
end
class Hash
def includes_hash?(other)
included = true
other.each do |key, value|
included &= self[key] == other[key]
end
included
end
end
This extends Hash by a method to find out if a Hash includes another (with multiple keys and values). Array is extended with the method you wanted, but it's a more generic approach since you can do this:
ary = [ {:a => 1, :b => 3, :c => 5}, {:a => 5, :b => 2, :c => 8} ]
ary.find_by_hash( { :a => 1, :c => 5 } )
Note: You should also consider using Symbols for Hash keys since it is a common practice in Ruby, but my approach does also work with your keys.
z = [{'a' => 1, 'b' => 2}, {'a' => 3, 'b' => 4}, {'a' => 1, 'b' => 4}]
class Array
def search_hash(hash)
key = hash.keys.first
value = hash.values.first
select { |h| h[key] == value }
end
end
z.search_hash({'a' => 3}) #=> [{"a"=>3, "b"=>4}]
or you can type it without curly brackets
z.search_hash('a' => 3)
Basically what you need is something like this:
class Array
def find_by_hash(h)
h.collect_concat do |key, value|
self.select{|h| h[key] == value}
end
end
end
I didn't find an approach in API, so I think we have to implement it of our own.
(by the way, I think #megas' approach is better and more readable)
Code by TDD:
class SearchHashTest < Test::Unit::TestCase
def setup
#array_with_hash_elements = ArrayWithHashElements.new [{'a' => 1, 'b' => 2}, {'a' => 3, 'b' => 4}, {'a' => 1, 'b' => 4}]
end
def test_search_an_array_by_hash_parameter_and_return_single_hash
assert_equal( {'a' => 3, 'b' => 4}, #array_with_hash_elements.search({'a'=>3}) )
end
def test_search_an_array_by_hash_parameter_and_return_an_array
assert_equal( [{'a' => 1, 'b' => 2}, {'a'=> 1, 'b' => 4}], #array_with_hash_elements.search({'a'=>1}))
end
end
implemented code ( just for demo, not production)
class ArrayWithHashElements
def initialize some_array
#elements = some_array
end
def search( query_hash)
puts "search: #{query_hash.inspect}"
result = []
#elements.each do | array_element_in_hash_form|
query_hash.each_pair do | key, value |
if array_element_in_hash_form.has_key?(key) && array_element_in_hash_form[key] == value
puts "adding : #{array_element_in_hash_form.inspect} to result"
result << array_element_in_hash_form
end
end
result
end
return result.size != 1 ? result : result[0]
end
end
result:
sg552#siwei-moto:~/workspace/test$ ruby search_hash_test.rb
Loaded suite search_hash_test
Started
search: {"a"=>1}
adding : {"b"=>2, "a"=>1} to result
adding : {"b"=>4, "a"=>1} to result
.search: {"a"=>3}
adding : {"b"=>4, "a"=>3} to result
.
Finished in 0.000513 seconds.
2 tests, 2 assertions, 0 failures, 0 errors
I have this:
h = { 1 => { 1 => {:a => "x", :b => "y", :c => "z"},
2 => {:a => "xx", :b => "yy", :c => "zz"}
},
2 => { 1 => {:a => "p", :b => "q", :c => "r"},
2 => {:a => "pp", :b => "qq", :c => "rr"}
}
}
I want to get this:
result = { 1 => { 1 => {:a => "x"},
2 => {:a => "xx"}
},
2 => { 1 => {:a => "p"},
2 => {:a => "pp"}
}
}
What would be a nice way of doing this ?
A single example can't really define your structure. For example, are hashes always 3 levels deep with hashes to be pruned at the level 3?
You can start with:
h.each{|k1,v1| v1.each{|k2, v2| v2.delete_if{|k3,v3| k3 != :a}}}
(Should really be a comment, but the code's hard to read that way)
If you're removing from the innermost hash all keys except :a, why not assign the value part of that hash directly to the hash that contains it?
result = {
1 => { 1 => "x", 2 => "xx"},
2 => { 1 => "p", 2 => "pp"}
}