Reputation: 351
Suppose I have this array of hashes:
[ {"nutrient"=>"protein", "value"=>12, "calories"=>48, "unit"=>"g"},
{"nutrient"=>"fat", "value"=>5, "calories"=>45, "unit"=>"g"},
{"nutrient"=>"fibre", "value"=>1, "calories"=>nil, "unit"=>"g"},
{"nutrient"=>"carbohydrates", "value"=>67, "calories"=>268, "unit"=>"g"},
{"nutrient"=>"calcium", "value"=>42, "calories"=>nil, "unit"=>"mg"}]
How can I return boolean true if (nutrient value is equal to 'carbohydrates' and it's calories are equal to 268 ) and (if nutrient value is equal to 'protein' and it's calories are equal to 48)
That is, in short I want to return true for above array of hash.
Upvotes: 3
Views: 2237
Reputation: 110675
Below, arr
is the given array of hashes (as in the example) and target
is a second array of hashes such as the following.
target = [{ "nutrient"=>"carbohydrates", "calories"=>268 },
{ "nutrient"=>"protein", "calories"=>48 }]
We wish to determine if, for all hashes h
in target
, there is a hash in arr
having the same key-value pairs as those that comprise h
. We can do that with the following method.
Code
def all_match?(arr, target)
target.all? { |h| arr.any? { |g| g.merge(h) == g } }
end
Examples
For the above value of target
, we obtain
all_match?(arr, target)
#=> true
Now let's modify target
so there is no match in arr
.
target[1]["calories"] = 50
#=> 50
target
#=> [{"nutrient"=>"carbohydrates", "calories"=>268},
# {"nutrient"=>"protein", "calories"=>50}]
all_match?(arr, target)
#=> false
Explanation
In the second example the steps are as follows. The first element of target is passed to the block, resulting in
h = target[0]
#=> {"nutrient"=>"carbohydrates", "calories"=>268}
and the block calculation is performed.
arr.any? { |g| g.merge(h) == g }
#=> true
As it happens, the matching hash is
g = arr[3]
#=>{"nutrient"=>"carbohydrates", "value"=>67, "calories"=>268, "unit"=>"g"},
g.merge(h) == g
#=> true
What I've done is merge h
into each hash in arr
until/if one is found, g
, that returns g
unchanged by the merge. For that to happen all key-value pairs in h
must be present in g
.
Because true
was returned, all?
passes the second element of target
to the block and the block calculation is performed.
h = target[1]
#=> {"nutrient"=>"protein", "calories"=>50}
arr.any? { |g| g.merge(h) == g }
#=> false
Having found a false
, all?
returns false
.
Upvotes: 1
Reputation: 6041
Perhaps this:
a.any? {|h| h['nutrient'] == 'carbohydrates' && h['calories'] == 268} &&
a.any? {|h| h['nutrient'] == 'proteins' && h['calories'] == 48}
# => true
Or as a nice finder method:
a = [ {"nutrient"=>"protein", "value"=>12, "calories"=>48, "unit"=>"g"}, {"nutrient"=>"fat", "value"=>5, "calories"=>45, "unit"=>"g"}, {"nutrient"=>"fibre", "value"=>1, "calories"=>nil, "unit"=>"g"}, {"nutrient"=>"carbohydrates", "value"=>67, "calories"=>268, "unit"=>"g"}, {"nutrient"=>"calcium", "value"=>42, "calories"=>nil, "unit"=>"mg"}]
def all?(array, *finders)
finders.all? do |finder|
array.any? { |hash| finder.all? { |k,v| hash[k] == v } }
end
end
puts all?(
a,
{'nutrient' => 'protein', 'value' => 12},
{'nutrient' => 'fat', 'calories' => 45}
).inspect
puts all?(
a,
{'nutrient' => 'protein', 'value' => 12},
{'nutrient' => 'fat', 'calories' => 46}
).inspect
Outputs:
true
false
The method will return false if a matching hash is not found from any of the hashes inside the array.
Upvotes: 1
Reputation: 306
a=[ {"nutrient"=>"protein", "value"=>12, "calories"=>48, "unit"=>"g"}, {"nutrient"=>"fat", "value"=>5, "calories"=>45, "unit"=>"g"}, {"nutrient"=>"fibre", "value"=>1, "calories"=>nil, "unit"=>"g"}, {"nutrient"=>"carbohydrates", "value"=>67, "calories"=>268, "unit"=>"g"}, {"nutrient"=>"calcium", "value"=>42, "calories"=>nil, "unit"=>"mg"}]
a.map! do |value|
if value["nutrient"].eql?'protein' and value["calories"].eql?268
true
elsif value["nutrient"].eql?'protein' and value["calories"].eql?48
true
else
false
end
end
puts a.inspect
Upvotes: -1
Reputation: 52357
a.count do |hash|
(hash['nutrient'] == 'carbohydrates' && hash['calories'] == 268) || (hash['nutrient'] == 'protein' && hash['calories'] == 48)
end == 2
What this does, is it counts any element in the collection, that passes EITHER this condition:
hash['nutrient'] == 'carbohydrates' && hash['calories'] == 268
OR this one
hash['nutrient'] == 'protein' && hash['calories'] == 48
And it returns true
if there are exactly two matches.
Upvotes: 1
Reputation: 114158
Assuming that the nutrient
values are unique, you could build a calories
hash via:
calories = a.each_with_object({}) { |e, h| h[e['nutrient']] = e['calories'] }
#=> {"protein"=>48, "fat"=>45, "fibre"=>nil, "carbohydrates"=>268, "calcium"=>nil}
And check for the values via:
calories['carbohydrates'] == 268 && calories['protein'] == 48
#=> true
Upvotes: 1