Find key/value pairs deep inside a hash containing an arbitrary number of nested hashes and arrays
Here's a simple recursive solution:
def nested_hash_value(obj,key)
if obj.respond_to?(:key?) && obj.key?(key)
obj[key]
elsif obj.respond_to?(:each)
r = nil
obj.find{ |*a| r=nested_hash_value(a.last,key) }
r
end
end
h = { foo:[1,2,[3,4],{a:{bar:42}}] }
p nested_hash_value(h,:bar)
#=> 42
On Array of Hashes need to count values while creating other key pairs same time in ruby
You could use Enumerable#group_by.
aoh.group_by { |h| h[:interface] }
.map do |k,a|
n = a.count { |h| h[:status] == "online" }
{ interface: k, online_hosts: n, offline_hosts: a.size - n }
end
#=> [{:interface=>"1A", :online_hosts=>2, :offline_hosts=>1},
# {:interface=>"2A", :online_hosts=>1, :offline_hosts=>2}]
Note:
aoh.group_by { |h| h[:interface] }
#=> {"1A"=>[{:interface=>"1A", :host=>"host_1", :status=>"online"},
# {:interface=>"1A", :host=>"host_2", :status=>"online"},
# {:interface=>"1A", :host=>"host_3", :status=>"offline"}],
# "2A"=>[{:interface=>"2A", :host=>"host_4", :status=>"offline"},
# {:interface=>"2A", :host=>"host_5", :status=>"offline"},
# {:interface=>"2A", :host=>"host_6", :status=>"online"}]}
Another way is to use the form of Hash#update (a.k.a. merge!
) that employs a block to determine the value of keys that are present in both hashes being merged. Here that block is do |_,o,n| ... end
. See the doc for the definitions of the three block variables, _
, o
and n
. (_
holds the common key. I've used an underscore to represent that variable to signal to the reader that it is not used in the block calculation, a common convention.)
aoh.each_with_object({}) do |g,h|
h.update(
g[:interface]=>
{ interface: g[:interface],
online_hosts: g[:status] == "online" ? 1 : 0,
offline_hosts: g[:status] == "online" ? 0 : 1
}
) do |_,o,n|
{ interface: o[:interface],
online_hosts: o[:online_hosts]+n[:online_hosts],
offline_hosts: o[:offline_hosts]+n[:offline_hosts]
}
end
end.values
#=> [{:interface=>"1A", :online_hosts=>2, :offline_hosts=>1},
# {:interface=>"2A", :online_hosts=>1, :offline_hosts=>2}]
Note that the receiver of Hash#values is:
{"1A"=>{:interface=>"1A", :online_hosts=>2, :offline_hosts=>1},
"2A"=>{:interface=>"2A", :online_hosts=>1, :offline_hosts=>2}}
search for key in a nested hash in Rails
Don't know if this is the best solution, but i would do:
h = {seal: 5, test: 3, answer: { nested: "damn", something: { email: "yay!" } } }
def search_hash(h, search)
return h[search] if h.fetch(search, false)
h.keys.each do |k|
answer = search_hash(h[k], search) if h[k].is_a? Hash
return answer if answer
end
false
end
puts search_hash(h, :email)
This will return the value if the key exists or false.
Reduce an array of nested hashes to a flattened array of key/value pairs
▶ flattener = ->(k, v) do
▷ case v
▷ when Enumerable then v.flat_map(&flattener)
▷ when NilClass then []
▷ else [k, v]
▷ end
▷ end
#⇒ #<Proc:0x000000032169e0@(pry):26 (lambda)>
▶ input.flat_map(&flattener).each_slice(2).to_a
#⇒ [
# [:created_at, "07/28/2017"],
# [:valid_record, "true"],
# [:gender, "m"],
# [:race, "w"],
# [:description, "possess"],
# [:a, "a"],
# [:b, "b"],
# [:c, "c"]
# ]
Is there a function in Ruby to traverse dynamic multi level hashes where all that is known is the required key?
The dig-deep gem seems to be what you are looking for:
require 'dig-deep'
scenario_1.dig_deep(:my_key)
=> "here"
scenario_2.dig_deep(:my_key)
=> "here"
Find multiple objects in nested hash
def nested_hash_values(obj,key)
r = []
if obj.is_a?(Hash)
r.push(obj[key]) if obj.key?(key)
obj.each_value { |e| r += nested_hash_values(e,key) }
end
if obj.is_a?(Array)
obj.each { |e| r += nested_hash_values(e,key) }
end
r
end
a = {"foo"=>["bar", "x", {"bar"=>["hello", {"foo"=>"world"}, "world!"], "foo"=>"BAR!"}, "enough?"], "bar"=>"foo"}
nested_hash_values(a, "foo")
=> [["bar", "x", {"bar"=>["hello", {"foo"=>"world"}, "world!"], "foo"=>"BAR!"}, "enough?"], "BAR!", "world"]
should return an array with all values found for a given key.
You could add additional if e.is_a?(Array) || e.is_a?(Hash)
in the each blocks. This would avoid unnecessary method calls and speed the function up a little, but add additional code.
How would I remove a nested value from a hash that occurs multiple times
Code
def defidder(h)
h.each_with_object({}) do |(k,v),h|
h[k] =
case v
when Array
v.reject { |s| s.match?(/\AFID\d+\z/) } if k == "within"
when Hash
defidder(v)
else
v
end
end
end
Example
I've added another layer of hash nesting to the example given in the question:
hash = {
"lock_version"=>4,
"exhibition_quality"=>false,
"within"=>["FID6", "S2"],
"repository"=>{
"ref"=>"/repositories/2",
"repository"=>{"ref"=>"/repositories/2"},
"within"=>["FID6", "S2"],
"1more"=>{ a: 1, "within"=>["FID999", "S7"] }
}
}
defidder hash
#=> {
# "lock_version"=>4,
# "exhibition_quality"=>false, "within"=>["S2"],
# "repository"=>{
# "ref"=>"/repositories/2",
# "repository"=>{"ref"=>"/repositories/2"},
# "within"=>["S2"],
# "1more"=>{:a=>1, "within"=>["S7"]
# }
# }
We may verify hash
was not mutated.
hash
#=> {
# "lock_version"=>4,
# "exhibition_quality"=>false,
# "within"=>["FID6", "S2"],
# "repository"=>{
# "ref"=>"/repositories/2",
# "repository"=>{"ref"=>"/repositories/2"},
# "within"=>["FID6", "S2"],
# "1more"=>{ a: 1, "within"=>["FID999", "S7"] }
# }
# }
add a key value pair to hash in first array depending on the substring from second array
But I get the error
This is because you do topic.first.keys
instead of topic.keys.first
; the difference is that topic.first
behaves like topic.to_a.first
, and Hash#to_a
returns an array of key/value pairs, e.g. [[k1, v1], [k2, v2], ...]
.
The below solution goes the other way around; it iterates over export_configs
, and then tries to find the corresponding topic. In your example, each export_config
hash comprises one key/value pair, but I don't see a reason why there can't be more, so this code does another loop inside.
It also makes use of Enumerable#find
to get an object out of an array that matches a certain predicate; this is useful for your problem in particular, despite it not being the fastest.
export_configs.map do |config|
# defensive: there could be more keys in config
config.map do |key, values|
# this operation scans topics each time, not very fast
topic = topics.find { |url| url.include? key }
# make a copy here, so that original variable isn't modified
values = values + [{ 'url' => topic }] if topic
[key, values]
end.to_h
end
Build paths of edge keys in nested hashes in ruby
def recurse(h)
h.flat_map do |k,v|
if v.is_a?(Hash)
recurse(v).map { |str| "%s.%s" % [k.to_s, str] }
else
k.to_s
end
end
end
h = {a: {m: {b: 2, c:1}, d: {e: {f: nil}, g: 3}}}
recurse(h)
#=> ["a.m.b", "a.m.c", "a.d.e.f", "a.d.g"]
h = {a: {m: {b: 2, c:1}, d: 5 }, e: {f: {g: nil}, h: 3}}
recurse(h)
#=> ["a.m.b", "a.m.c", "a.d", "e.f.g", "e.h"]
Efficient way of matching 2 hashes having maximum similarity
sqlfiddle I have put the table here with the same data as above. I have created a view from many different tables.
Do it in SQL, this is what it's good at.
Use a self-join to get the number of overlaps for each pair.
select
a.emp_id emp_id1,
b.emp_id emp_id2,
count(a.option_id) as overlap
from data a
join data b on
-- Ensure we count each pair only once
a.emp_id < b.emp_id and
a.option_id = b.option_id
group by a.emp_id, b.emp_id
Then use that as a CTE to select the pairs with the most overlaps.
with overlaps as (
select
a.emp_id emp_id1,
b.emp_id emp_id2,
count(a.option_id) as overlap
from data a
join data b on
a.emp_id < b.emp_id and
a.option_id = b.option_id
group by a.emp_id, b.emp_id
)
select *
from overlaps
where overlap = (
select max(overlap)
from overlaps
)
So long as you're indexed, that should perform much better than pulling all the data out into Ruby.
create index idx_option_emp_ids on data(option_id, emp_id);
Even without indexes, it should perform much better than pulling it all out into Ruby.
Related Topics
What Is the Use of Gemfile in Rails
Ruby Method Lookup Path For an Object
How to Get Filename Without Extension from File Path in Ruby
Ruby 1.9.2 How to Install Rmagick on Windows
"Gem Install Rails" Fails With Dns Error
Why Does White-Space Affect Ruby Function Calls
Error Sudo: Gem: Command Not Found
How Does Array#Map Have Parameter to Do Something Like This
What Are Some Good Ruby-Based Web Crawlers
How to Search a Folder and All of Its Subfolders For Files of a Certain Type
Why Use "Self" to Access Activerecord/Rails Model Properties
Extract a Substring from a String in Ruby Using a Regular Expression
Getaddrinfo: Nodename Nor Servname Provided, or Not Known
Json Encoding Wrongly Escaped (Rails 3, Ruby 1.9.2)
What's the Precedence of Ruby'S Method Call