Making Hashes Find Each Other by Their Values Using Ruby

How to find a hash key containing a matching value

You could use Enumerable#select:

clients.select{|key, hash| hash["client_id"] == "2180" }
#=> [["orange", {"client_id"=>"2180"}]]

Note that the result will be an array of all the matching values, where each is an array of the key and value.

Find values in array of hashes to create new array of hashes

Pure ruby

array = [{:a=>'value1', :b=>'value2', :c=>'value3', :d=>'value4'}, {:a=>'value1', :b=>'value2', :c=>'value3', :d=>'value4'}]

b_and_c_array = array.map{|a| a.select{|k, _| [:b, :c].include?(k)} }

We take each hash using the map method that will return a result array. For each hash, we select only [:b, :c] keys. You can add more inside it.

Rails

If using Rails, let's use Hash#slice, prettier :

b_and_c_array = array.map{|a| a.slice(:b, :c) }

Ruby how to find each value in hash greater than x?

You can use:

months.select { |_month, days| days > 30 }    

This former gives you all results that fit the criteria (days > 30).

Here are the docs:

  • select

Once you've got the values you need, you can then print them to the console (or output however you'd like), e.g.

long_months = months.select { |_month, days| days > 30 }
long_months.each { |month, days| puts "#{month} has #{days} days" }

All that being said, assigning to a value before printing the result means two loops, whereas this can be achieved in one using a simple each:

months.each do |month, days|
puts("#{month} has #{days} days") if days > 30
end

That'll be more efficient as there's less churn involved :)

How do I search within an array of hashes by hash values in ruby?

You're looking for Enumerable#select (also called find_all):

@fathers.select {|father| father["age"] > 35 }
# => [ { "age" => 40, "father" => "Bob" },
# { "age" => 50, "father" => "Batman" } ]

Per the documentation, it "returns an array containing all elements of [the enumerable, in this case @fathers] for which block is not false."

How to find and return a hash value within an array of hashes, given multiple other values in the hash

You're on the right track!

results.find {|i| i["day"] == "2012-08-15" and i["name"] == "Bill"}["calls"]
# => "8"

Get hash values (from a number of hashes) in an array and sum them up

To sum the values of a hash:

{a:1,b:2}.values.sum
#=> 3

And to do that for your array of hashes:

total_order.flat_map(&:values).sum
#=> 15.0

Note that sum is only available in Ruby 2.4.0 and later (and in Rails). In older versions you can use reduce(:+) and inject(:+) instead of sum.

i want to make a group Hash by keys and add values

I'd use the each_with_object method.
(key, value) here is a deconstruction of each key/value pair like 93=>1, hash is an intermediate object to store the result.

data.each_with_object({}) do |(key, value), hash|
result_key =
case key
when 10..19 then 1
when 20..29 then 2
when 90..99 then 9
end
next if result_key.nil?
hash[result_key] ||= 0
hash[result_key] += value
end

For the provided input I got {9=>2, 2=>7856, 1=>267}

UPD

A shorter solution suggested by Holger Just and Stefan in the comments below.

data.each_with_object(Hash.new(0)) do |(key, value), hash|
hash[key / 10] += value
end

With Hash.new(0) the initial object will be a hash with the default value 0

> hash = Hash.new(0)
=> {}
> hash[1]
=> 0

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}}


Related Topics



Leave a reply



Submit