A hash using lambdas without relying on conditionals or enumerators - ruby

If I were to have a list like so of accepted aliases and root names for basic colors:
coloraliases = {
["red", "crimson", "auburn", "rose", "maroon", "burgundy"] => "red",
["blue", "teal", "aqua", "azure", "cobalt"] => "blue",
["green", "emerald", "absinthe", "avocado", "lime"] => "green",
["yellow", "banana", "lemon", "gold", "citrine"] => "yellow"
}
I couldn't just do this:
coloraliases["crimson"]
#=> "red"
I'm trying to coax this behavior like so:
basecolor = lambda do |str|
x = nil
coloraliases.each do |keys, value|
if keys.include?(str)
x = value
break
end
end# of coloraliases hash
x
end
which should work as expected.
Am wondering if there are any more elegant ways to do this, specifically ways not involving conditional blocks or even enumerators. Ternary operators are ok or better because they're compact but still not preferable because they're conditionals.

Your hash design is wrong. You are not using it in a way a hash is supposed to be used. It should be:
coloraliases = {
"red" => "red",
"crimson" => "red",
"auburn" => "red",
"rose" => "red",
"maroon" => "red",
"burgundy" => "red",
"blue" => "blue",
"teal" => "blue",
"aqua" => "blue",
"azure" => "blue",
"cobalt" => "blue",
...
}
And then, you would simply get:
coloraliases["crimson"] # => "red"

Related

Mapping one hash value to other hash ruby

I have a hash which gives me the data in following manner:
details = [{"severity_longevity" => "Medium", "operating_leverage" => "High",
"financial_leverage"=> "Low", "revenue_growth"=> "Low"}]
I have one hash which gives me the score that I am supposed to assign.
score = [{"Low"=> 5},{"Medium"=> 10}, {"High"=> 15}]
How can I change the "Medium" "Low" and "High" in details hash with their number scores from
score hash ?
For Hashes you can use transform_values method
details = {
"severity_longevity" => "Medium",
"operating_leverage" => "High",
"financial_leverage"=> "Low",
"revenue_growth"=> "Low"
}
score = {"Low" => 5, "Medium" => 10, "High" => 15}
updated = details.transform_values { |v| score[v] }
# => { "severity_longevity" => 10, ... }

In Ruby, what's the advantage of #each_pair over #each when iterating through a hash?

Let's say I want to access the values of a hash like this:
munsters = {
"Herman" => { "age" => 32, "gender" => "male" },
"Lily" => { "age" => 30, "gender" => "female" },
"Grandpa" => { "age" => 402, "gender" => "male" },
"Eddie" => { "age" => 10, "gender" => "male" },
"Marilyn" => { "age" => 23, "gender" => "female"}
}
I could use #each with two parameters:
munsters.each do |key, value|
puts "#{name} is a #{value["age"]}-year-old #{value["gender"]}."
end
Or I could use #each_pair with two parameters:
munsters.each_pair do |key, value|
puts "#{name} is a #{value["age"]}-year-old #{value["gender"]}."
end
Perhaps the difference between the two is not borne out in this simple example, but can someone help me to understand the advantage of using #each_pair over #each ?
Because Hash is an Enumerable, it has to have an each method. each_pair may be a clearer name, since it strongly suggests that two-element arrays containing key-value pairs are passed to the block.
They are aliases for each other: they share the same source code.

Filtering Ruby array of hashes by another hash

Perhaps I'm missing something obvious. It seems tricky to filter a hash by another hash or multiple key/value pairs.
fruit = [
{ name: "apple", color: "red", pieable: true },
{ name: "orange", color: "orange", pieable: false },
{ name: "grape", color: "purple", pieable: false },
{ name: "cherry", color: "red", pieable: true },
{ name: "banana", color: "yellow", pieable: true },
{ name: "tomato", color: "red", pieable: false }
]
filter = { color: "red", pieable: true }
# some awesome one-liner? would return
[
{ name: "apple", color: "red", pieable: true },
{ name: "cherry", color: "red", pieable: true }
]
The array of hashes I don't think is the problem. I don't even know how to test a hash by another arbitrary hash. I'm using Rails so anything out of active_support etc is fine.
COULD be made into a one liner. But multi-line is cleaner.
fruit.select do |hash| # or use select!
filter.all? do |key, value|
value == hash[key]
end
end
If you allow two lines it could also be made into an efficient "one-liner" like so:
keys, values = filter.to_a.transpose
fruit.select { |f| f.values_at(*keys) == values }
Not the most efficient (you could just use an array form of filter to avoid repeated conversions), but:
fruit.select {|f| (filter.to_a - f.to_a).empty? }
I'd be inclined to use Enumerable#group_by for this:
fruit.group_by { |g| { color: g[:color], pieable: g[:pieable] } }[filter]
#=> [{:name=>"apple", :color=>"red", :pieable=>true},
# {:name=>"cherry", :color=>"red", :pieable=>true}]
Tony Arcieri (#bascule) gave this really nice solution on twitter.
require 'active_support/core_ext' # unneeded if you are in a rails app
fruit.select { |hash| hash.slice(*filter.keys) == filter }
And it works.
# [{:name=>"apple", :color=>"red", :pieable=>true},
# {:name=>"cherry", :color=>"red", :pieable=>true}]
Try this
fruit.select{ |hash| (filter.to_a & hash.to_a) == filter.to_a }
=> [{:name=>"apple", :color=>"red", :pieable=>true},
{:name=>"cherry", :color=>"red", :pieable=>true}]

Remove all but one duplicate from array of hashes

I have an array of hashes like this:
[
{ :color => 'red', :animal => 'dog' },
{ :color => 'blue', :animal => 'cat' },
{ :color => 'yellow', :animal => 'frog' },
{ :color => 'red', :animal => 'cat' },
{ :color => 'red', :animal => 'mouse' }
]
What I want to do is remove all but one of the duplicates based on one of the keys.
So in this case, I want to remove all but one of the items where color is red. Doesn't matter which one.
Final output would be something like this:
[
{ :color => 'blue', :animal => 'cat' },
{ :color => 'yellow', :animal => 'frog' },
{ :color => 'red', :animal => 'mouse' }
]
Again, when removing the duplicates, the one to keep does not matter.
.group_by { |x| x[:color] }.values.map(&:first)
.inject({}) { |xs, x| xs[x[:color]] = x; xs }.values
Another way of achieving this would be
.uniq { |h| h[:color] }
=> [{:color=>"red", :animal=>"dog"}, {:color=>"blue",
:animal=>"cat"}, {:color=>"yellow", :animal=>"frog"}]
As #Victor suggested this is for ruby 1.9.2+

Group by and format array of hash values in ruby

Hey I have an array of hash values as follows.
[{"group" => "1", "message" => "hey", "weight" => 1}, {"group" => "1", "message"
=> "hey1", "weight" => 2}, {"group" => "2", "message" => "hey3", "weight" => 4}]
I want to group_by group and format it so that I get the following:
[{"group" => 1, "messages" => {"hey","hey1"}, "weights" => {1,2}}, {"group" => 2,
"messages" => {"hey3"}, "weights" => {4}}]
Is there a nice ruby way to achieve this?
Edit: Now I have:
[
{"group" => "1", "message" => {"hey" => "1"}},
{"group" => "1", "message" => {"hey1" => "2"}}
]
I'd like to have
{"group" => "1", "messages" => {"hey1" => "1", "hey2" => "2"} }
Based on your revised question:
groups = [
{"group" => "1", "message" => {"hey" => "1"}},
{"group" => "1", "message" => {"hey1" => "2"}}
]
merged = groups.inject do |h1,h2|
h1.merge(h2) do |k,v1,v2|
if v1==v2
v1
elsif v1.is_a?(Hash) && v2.is_a?(Hash)
v1.merge(v2)
else
[*v1,*v2]
end
end
end
p merged
#=> {"group"=>"1", "message"=>{"hey"=>"1", "hey1"=>"2"}}
I think the output you want is:
[{"messages"=>["hey", "hey1"], "weights"=>[1, 2], "group"=>"1"}, {"messages"=>["hey3"], "weights"=>[4], "group"=>"2"}]
If this is the case, this code does what you want:
h.group_by { |item| item["group"] }.values.map do |item|
item.inject({"messages" => [], "weights" => []}) do |result, subitem|
result["group"] = subitem["group"]
result["messages"] << subitem["message"]
result["weights"] << subitem["weight"]
result
end
end
You should be able to improve it knowing more about your specific problem, but it should be a good starting point.

Resources