group_by multiple times in ruby - ruby

I have an array of hashes called events:
events = [
{:name => "Event 1", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "A"},
{:name => "Event 2", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "A"},
{:name => "Event 3", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "B"},
{:name => "Event 4", :date => "2019-02-21 08:00:00", :area => "South", :micro_area => "B"},
{:name => "Event 5", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "A"},
{:name => "Event 6", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "A"},
{:name => "Event 7", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "B"},
{:name => "Event 8", :date => "2019-02-21 08:00:00", :area => "North", :micro_area => "B"}
]
I want to know how to group_by first date, then area then micro_area to end up with a single array of hashes for example:
[
{
"2019-02-21 08:00:00": {
"South": {
"A": [
{:name=>"Event 1", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A" },
{:name=>"Event 2", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A" }
],
"B": [
{:name=>"Event 3", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"B" },
{:name=>"Event 4", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"B" }
]
},
"North": {
"A": [
{:name=>"Event 5", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"A" },
{:name=>"Event 6", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"A" }
],
"B": [
{:name=>"Event 7", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"B" },
{:name=>"Event 8", :date=>"2019-02-21 08:00:00", :area=>"North", :micro_area=>"B" }
]
}
}
}
]
Trying events.group_by { |r| [r[:date], r[:area], r[:micro_area]] } doesn't seem too work the way I want it to.

I think following will work for you,
events = [
{ name: 'Event 1', date: '2019-02-21 08:00:00', area: 'South', micro_area: 'A' }
]
events.group_by { |x| x[:date] }.transform_values do |v1|
v1.group_by { |y| y[:area] }.transform_values do |v2|
v2.group_by { |z| z[:micro_area] }
end
end
# {
# "2019-02-21 08:00:00"=>{
# "South"=>{
# "A"=>[
# {:name=>"Event 1", :date=>"2019-02-21 08:00:00", :area=>"South", :micro_area=>"A"}
# ]
# }
# }
# }

Another option is to build the nested structure as you traverse your hash:
events.each_with_object({}) do |event, result|
d, a, m = event.values_at(:date, :area, :micro_area)
result[d] ||= {}
result[d][a] ||= {}
result[d][a][m] ||= []
result[d][a][m] << event
end

Another option is grouping them like you did in the question. Then build the nested structure from the array used as key.
# build an endless nested structure
nested = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) }
# group by the different criteria and place them in the above nested structure
events.group_by { |event| event.values_at(:date, :area, :micro_area) }
.each { |(*path, last), events| nested.dig(*path)[last] = events }
# optional - reset all default procs
reset_default_proc = ->(hash) { hash.each_value(&reset_default_proc).default = nil if hash.is_a?(Hash) }
reset_default_proc.call(nested)
The above leaves the answer in the nested variable.
References:
Hash::new to create the nested hash.
Hash#default_proc to get the default proc of a hash.
Hash#default= to reset the hash default back to nil.
Hash#dig to traverse the nested structure until the last node.
Hash#[]= to set the last node equal to the grouped events.
Array decomposition and array to argument conversion to capture all but the last node into path and call #dig with the contents of path as arguments.

Here is a recursive solution that will handle arbitrary levels of nesting and arbitrary grouping objects.
def hashify(events, grouping_keys)
return events if grouping_keys.empty?
first_key, *remaining_keys = grouping_keys
events.group_by { |h| h[first_key] }.
transform_values { |a|
hashify(a.map { |h|
h.reject { |k,_| k == first_key } },
remaining_keys) }
end
Before executing this with the sample data from the questions let's add a hash with a different date to events.
events <<
{ :name=>"Event 8", :date=>"2018-12-31 08:00:00",
:area=>"North", :micro_area=>"B" }
grouping_keys = [:date, :area, :micro_area]
hashify(events, grouping_keys)
#=> {"2019-02-21 08:00:00"=>{
# "South"=>{
# "A"=>[{:name=>"Event 1"}, {:name=>"Event 2"}],
# "B"=>[{:name=>"Event 3"}, {:name=>"Event 4"}]
# },
# "North"=>{
# "A"=>[{:name=>"Event 5"}, {:name=>"Event 6"}],
# "B"=>[{:name=>"Event 7"}, {:name=>"Event 8"}]
# }
# },
# "2018-12-31 08:00:00"=>{
# "North"=>{
# "B"=>[{:name=>"Event 8"}]
# }
# }
# }
hashify(events, [:date, :area])
#=> {"2019-02-21 08:00:00"=>{
# "South"=>[
# {:name=>"Event 1", :micro_area=>"A"},
# {:name=>"Event 2", :micro_area=>"A"},
# {:name=>"Event 3", :micro_area=>"B"},
# {:name=>"Event 4", :micro_area=>"B"}
# ],
# "North"=>[
# {:name=>"Event 5", :micro_area=>"A"},
# {:name=>"Event 6", :micro_area=>"A"},
# {:name=>"Event 7", :micro_area=>"B"},
# {:name=>"Event 8", :micro_area=>"B"}
# ]
# },
# "2018-12-31 08:00:00"=>{
# "North"=>[
# {:name=>"Event 8", :micro_area=>"B"}
# ]
# }
# }
See Enumerable#group_by, Hash#transform_values and Hash#reject.

Related

map an array found in a hash to individual hash values

I'm getting data from an API and need to format it differently. I have a car_array that consists of an array of hashes. However, sometimes there will be a sub-array as one of the hash values that contains more than 1 element. In this case, there should be a loop so that each element in the array gets mapped correctly as separate entries.
An example of data, note how prices and options_package are arrays with multiple elements.
[{
dealer_id: 1,
dealer_name: "dealership 1",
car_make: "jeep",
prices: ['30', '32', '35'],
options_package: ['A', 'B', 'C']
}, {
dealer_id: 2,
dealer_name: "dealership 2",
car_make: "ford",
prices: ['50', '55'],
options_package: ['X', 'Y']
}, {
dealer_id: 3,
dealer_name: "dealership 3",
car_make: "dodge",
prices: ['70'],
options_package: ['A']
}]
I would like to create multiple entries when there are multiple array elements
for example, the data above should be broken out and mapped as:
some_array = [
{ dealer_id: 1, dealer_name: "dealership 1", car_make: "jeep", price: '30', options_package: 'A' },
{ dealer_id: 1, dealer_name: "dealership 1", car_make: "jeep", price: '32', options_package: 'B' },
{ dealer_id: 1, dealer_name: "dealership 1", car_make: "jeep", price: '35', options_package: 'C' },
{ dealer_id: 2, dealer_name: "dealership 2", car_make: "ford", price: '50', options_package: 'X' },
{ dealer_id: 2, dealer_name: "dealership 2", car_make: "ford", price: '55', options_package: 'Y' },
{ dealer_id: 3, dealer_name: "dealership 3", car_make: "dodge", price: '70', options_package: 'A' }
]
Here's what I've got so far:
car_arr.each do |car|
if car['Prices'].length > 1
# if there are multiple prices/options loop through each one and create a new car
car.each do |key, value|
if key == 'Prices'
value.each do |price|
formatted_car_array << {
dealer_id: car['dealer_id'],
dealer_name: car['dealer_name'],
car_make: car['make'],
options_package: ???????,
price: price,
}
end
end
end
else
# there's only element for price and options_package
formatted_car_array << {
dealer_id: car['dealer_id'],
dealer_name: car['dealer_name'],
car_make: car['make'],
options_package: car['options_package'],
price: car['prices']
}
end
end
Consider just one hash to start with, and how this problem can be solved for this simpler problem.
h = {
dealer_id: 1,
dealer_name: "dealership 1",
car_make: "jeep",
prices: ['30', '32', '35'],
options_package: ['A', 'B', 'C']
}
Let's get combinations of prices and options packages using #zip.
h[:prices].zip(h[:options_package])
# => [["30", "A"], ["32", "B"], ["35", "C"]]
The length of 3 for this array corresponds with how many hashes we expect to get from it, so let's map over those, building a new hash each time.
h[:prices].zip(h[:options_package]).map do |price, pkg|
{
dealer_id: h[:dealer_id],
dealer_name: h[:dealer_name],
car_make: h[:car_make],
price: price,
options_package: pkg,
}
end
# => [{:price=>"30", :options_package=>"A", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"},
# {:price=>"32", :options_package=>"B", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"},
# {:price=>"35", :options_package=>"C", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"}]
Now you just need to #flat_map this over your array.
car_arr.flat_map do |h|
h[:prices].zip(h[:options_package]).map do |price, pkg|
{
dealer_id: h[:dealer_id],
dealer_name: h[:dealer_name],
car_make: h[:car_make],
price: price,
options_package: pkg,
}
end
end
# => [{:price=>"30", :options_package=>"A", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"},
# {:price=>"32", :options_package=>"B", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"},
# {:price=>"35", :options_package=>"C", :dealership=>nil, :dealer_id=>1, :car_make=>"jeep"},
# {:price=>"50", :options_package=>"X", :dealership=>nil, :dealer_id=>2, :car_make=>"ford"},
# {:price=>"55", :options_package=>"Y", :dealership=>nil, :dealer_id=>2, :car_make=>"ford"},
# {:price=>"70", :options_package=>"A", :dealership=>nil, :dealer_id=>3, :car_make=>"dodge"}]
If arr is the array of hashes given in the question one may write the following.
arr.flat_map do |h|
h[:prices].zip(h[:options_package]).map do |p,o|
h.reject { |k,_| k == :prices }.merge(price: p, options_package: o)
end
end
#=> [{:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep",
# :options_package=>"A", :price=>"30"},
# {:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep",
# :options_package=>"B", :price=>"32"},
# {:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep",
# :options_package=>"C", :price=>"35"},
# {:dealer_id=>2, :dealer_name=>"dealership 2", :car_make=>"ford",
# :options_package=>"X", :price=>"50"},
# {:dealer_id=>2, :dealer_name=>"dealership 2", :car_make=>"ford",
# :options_package=>"Y", :price=>"55"},
# {:dealer_id=>3, :dealer_name=>"dealership 3", :car_make=>"dodge",
# :options_package=>"A", :price=>"70"}]
Note that this code need not be changed if key-value pairs are added to the hash or if the keys :dealer_id and :dealer_name are deleted or renamed.
My answer is an extension upon the answer of Chris.
car_arr.flat_map do |h|
h[:prices].zip(h[:options_package]).map do |price, pkg|
{
dealer_id: h[:dealer_id],
dealer_name: h[:dealer_name],
car_make: h[:car_make],
price: price,
options_package: pkg,
}
end
end
This answer could be shortened if you're using the newest Ruby version.
Ruby 3.0 introduced Hash#except, which lets you easily create a copy of a hash without the specified key(s). This allows us to reduce the answer to:
cars.flat_map do |car|
car[:prices].zip(car[:options_package]).map do |price, pkg|
car.except(:prices).merge(price: price, options_package: pkg)
end
end
car.except(:prices) will create a new hash without the :prices key/value-pair, we don't need to remove :options_package, since merge will overwrite the the old :options_package value with a new value.
Ruby 3.1 introduced { x:, y: } as syntax sugar for { x: x, y: y }. This allows us to "reduce" the answer further to:
cars.flat_map do |car|
car[:prices].zip(car[:options_package]).map do |price, options_package|
car.except(:prices).merge(price:, options_package:)
end
end
Input
input = [{
dealer_id: 1,
dealer_name: "dealership 1",
car_make: "jeep",
prices: ['30', '32', '35'],
options_package: ['A', 'B', 'C']
}, {
dealer_id: 2,
dealer_name: "dealership 2",
car_make: "ford",
prices: ['50', '55'],
options_package: ['X', 'Y']
}, {
dealer_id: 3,
dealer_name: "dealership 3",
car_make: "dodge",
prices: ['70'],
options_package: ['A']
}]
Code
result = input.map do |h|
prices = h[:prices]
options_package = h[:options_package]
h[:options_package].count.times.map do
h[:prices] = prices.shift
h[:options_package] = options_package.shift
h.dup
end
end
p result.flatten
Output
[{:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep", :prices=>"30", :options_package=>"A"},
{:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep", :prices=>"32", :options_package=>"B"},
{:dealer_id=>1, :dealer_name=>"dealership 1", :car_make=>"jeep", :prices=>"35", :options_package=>"C"},
{:dealer_id=>2, :dealer_name=>"dealership 2", :car_make=>"ford", :prices=>"50", :options_package=>"X"},
{:dealer_id=>2, :dealer_name=>"dealership 2", :car_make=>"ford", :prices=>"55", :options_package=>"Y"},
{:dealer_id=>3, :dealer_name=>"dealership 3", :car_make=>"dodge", :prices=>"70", :options_package=>"A"}]

Delete duplicated elements in an array that's a value in a hash and its corresponding ids

I have a hash with values that's an array. How do I delete repeated elements in the array and the corresponding ids in the most performant way?
Here's an example of my hash
hash = {
"id" => "sjfdkjfd",
"name" => "Field Name",
"type" => "field",
"options" => ["Language", "Question", "Question", "Answer", "Answer"],
"option_ids" => ["12345", "23456", "34567", "45678", "56789"]
}
The idea I have is something like this
hash["options"].each_with_index { |value, index |
h = {}
if h.key?(value)
delete(value)
delete hash["option_ids"].delete_at(index)
else
h[value] = index
end
}
The result should be
hash = {
"id" => "sjfdkjfd",
"name" => "Field Name",
"type" => "field",
"options" => ["Language", "Question", "Answer"],
"option_ids" => ["12345", "23456", "45678"]
}
I know I have to put into consideration that when I delete the values of the options and option_ids the indexes of those values are going to change. But not sure how to do this
The first idea I had is to zip the values and call uniq, then think a way to return back to the initial form:
h['options'].zip(h['option_ids']).uniq(&:first).transpose
#=> [["Language", "Question", "Answer"], ["12345", "23456", "45678"]]
Then, via parallel assignment:
h['options'], h['option_ids'] = h['options'].zip(h['option_ids']).uniq(&:first).transpose
h #=> {"id"=>"sjfdkjfd", "name"=>"Field Name", "type"=>"field", "options"=>["Language", "Question", "Answer"], "option_ids"=>["12345", "23456", "45678"]}
These are the steps:
h['options'].zip(h['option_ids'])
#=> [["Language", "12345"], ["Question", "23456"], ["Question", "34567"], ["Answer", "45678"], ["Answer", "56789"]]
h['options'].zip(h['option_ids']).uniq(&:first)
#=> [["Language", "12345"], ["Question", "23456"], ["Answer", "45678"]]
hash = {
"id" => "sjfdkjfd",
"name" => "Field Name",
"type" => "field",
"options" => ["L", "Q", "Q", "Q", "A", "A", "Q"],
"option_ids" => ["12345", "23456", "34567", "dog", "45678", "56789", "cat"]
}
I assume that "repeated elements" refers to contiguous equal elements (2 only in [1,2,2,1]) as opposed to "duplicated elements" (both 1 and 2 in the previous example). I do show how the code would be altered (simplified, in fact) if the second interpretation applies.
idx = hash["options"].
each_with_index.
chunk_while { |(a,_),(b,_)| a==b }.
map { |(_,i),*| i }
#=> [0, 1, 4, 6]
hash.merge(
["options", "option_ids"].each_with_object({}) { |k,h| h[k] = hash[k].values_at(*idx) }
)
#=> {"id"=>"sjfdkjfd",
# "name"=>"Field Name",
# "type"=>"field",
# "options"=>["L", "Q", "A", "Q"],
# "option_ids"=>["12345", "23456", "45678", "cat"]}
If "repeated elements" is interpreted to mean that the values of "options" and "option_ids" are to only have the first three elements shown above, calculate idx as follows:
idx = hash["options"].
each_with_index.
uniq { |s,_| s }.
map(&:last)
#=> [0, 1, 4]
See Enumerable#chunk_while (Enumerable#slice_when could be used instead) and Array#values_at. The steps are as follows.
a = hash["options"]
#=> ["L", "Q", "Q", "Q", "A", "A", "Q"]
e0 = a.each_with_index
#=> #<Enumerator: ["L", "Q", "Q", "Q", "A", "A", "Q"]:each_with_index>
e1 = e0.chunk_while { |(a,_),(b,_)| a==b }
#=> #<Enumerator: #<Enumerator::Generator:0x000055e4bcf17740>:each>
We can see the values the enumerator e1 will generate and pass to map by converting it to an array:
e1.to_a
#=> [[["L", 0]],
# [["Q", 1], ["Q", 2], ["Q", 3]],
# [["A", 4], ["A", 5]], [["Q", 6]]]
Continuing,
idx = e1.map { |(_,i),*| i }
#=> [0, 1, 4, 6]
c = ["options", "option_ids"].
each_with_object({}) { |k,h| h[k] = hash[k].values_at(*idx) }
#=> {"options"=>["L", "Q", "A", "Q"],
# "option_ids"=>["12345", "23456", "45678", "cat"]}
hash.merge(c)
#=> {"id"=>"sjfdkjfd",
# "name"=>"Field Name",
# "type"=>"field",
# "options"=>["L", "Q", "A", "Q"],
# "option_ids"=>["12345", "23456", "45678", "cat"]}
Using Array#transpose
hash = {
"options" => ["Language", "Question", "Question", "Answer", "Answer"],
"option_ids" => ["12345", "23456", "34567", "45678", "56789"]
}
hash.values.transpose.uniq(&:first).transpose.map.with_index {|v,i| [hash.keys[i], v]}.to_h
#=> {"options"=>["Language", "Question", "Answer"], "option_ids"=>["12345", "23456", "45678"]}
After the OP edit:
hash = {
"id" => "sjfdkjfd",
"name" => "Field Name",
"type" => "field",
"options" => ["Language", "Question", "Question", "Answer", "Answer"],
"option_ids" => ["12345", "23456", "34567", "45678", "56789"]
}
hash_array = hash.to_a.select {|v| v.last.is_a?(Array)}.transpose
hash.merge([hash_array.first].push(hash_array.last.transpose.uniq(&:first).transpose).transpose.to_h)
#=> {"id"=>"sjfdkjfd", "name"=>"Field Name", "type"=>"field", "options"=>["Language", "Question", "Answer"], "option_ids"=>["12345", "23456", "45678"]}

Outputting the keys of the max value of a hash within Arrays in Ruby using methods

data = [
"Company one" => {
"number_1" => 46,
"number_2" => 3055,
"country" => "USA"
},
"Company two" => {
"number_1" => 32,
"number_2" => 6610,
"country" => "USA"
},
"Company three" => {
"number_1" => 40,
"number_2" => 9128,
"country" => "USA"
}
]
So I have this array in which I'm trying to get which of the company has the biggest number in 'number_2'. The largest would be Company three with 9128.
So I have this code that puts the largest number which would be 9128
def number(data)
collected_array=[]
data.each do |company_hash|
collected_array = company_hash.map do |k,v|
v["number_2"]
end
end
puts collected_array.max
end
number(data)
But I'm trying to puts the company name with the largest number which would be "Company three". I've tried .keys and other ways but it gives me error.
I've tried this way:
def number(data)
collected_array=[]
data.each do |company_hash|
collected_array = company_hash.map do |k,v|
v["number_2"]
k
end
end
puts collected_array.max
end
number(data)
but it gives me "Company two" rather than "Company three" which would be the company with the highest number
As stated by #Cary, it can be simplified accessing the first element on data, and there using max_by, on the hash local variable available within the block checking the number_2 key value.
As the result is an Array containing two elements, the first one is the company name, the second and last one, the hash containing its data:
data = [
"Company one" => {
"number_1" => 46,
"number_2" => 3055,
"country" => "USA"
},
"Company two" => {
"number_1" => 32,
"number_2" => 6610,
"country" => "USA"
},
"Company three" => {
"number_1" => 40,
"number_2" => 9128,
"country" => "USA"
}
]
max_company = data.first.max_by { |_, h| h['number_2'] }
p max_company.first # "Company three"
p max_company.last['number_2'] # 9128

Getting the hash key whose value array contains a given string

I have the following code:
country_code = infer_country # will grab a user's two character country code
region = 'us' # united states by default
region_map = {
"au" => ["au"], # australia
"al" => ["al", "ba", "bg", "hr", "md", "me", "mk", "ro", "si"], # bulgaria and the balkans
"cn" => ["cn"], # china
"ee" => ["ee", "lt", "lv"], # estonia and the baltics
"fi" => ["fi"], # finland
"at" => ["at", "ch", "de"], # germany, austria, switzerland
"cy" => ["cy", "gr", "mt"], # greece, cyprus, malta
"hk" => ["hk"], # hong_kong
"id" => ["id"], # indonesia
"it" => ["it"], # italy
"jp" => ["jp"], # japan
"kp" => ["kp", "kr"], # korea
"ar" => ["ar", "bl", "bo", "br", "bz", "cl", "co", "cr", "cu", "do", "ec", "gf", "gp", "gt", "hn",
"ht", "mf", "mq", "mx", "ni", "pa", "pe", "pr", "py", "sv", "uy", "ve"], # latin america including brazil
"my" => ["my"], # malaysia
"af" => ["af", "eg", "iq", "ir", "sa", "ye", "sy", "il", "jo", "ps", "lb", "om", "kw", "qa", "bh"], # middle east
"nl" => ["nl"], # netherlands
"no" => ["no"], # norway
"pl" => ["pl"], # poland
"pt" => ["pt"], # portugal
"ph" => ["ph"], # philippines
"ru" => ["ru"], # russia
"rs" => ["rs"], # serbia
"sg" => ["sg"], # singapore
"za" => ["za"], # south africa
"bn" => ["bn", "bu", "kh", "la", "tl", "vn"], # south east asia
"es" => ["es"], # spain
"tw" => ["tw"], # taiwan
"th" => ["th"], # thailand
"tr" => ["tr"], # turkey
"gb" => ["gb" ] # united kingdom
}.invert
# version 0.0
region_map.each do |key, value|
if key.include? country_code
region = value
break
end
end
puts region
If country_code is "gb", then "gb" should be printed out. If country_code is in south east asia, say it's "vn", then "bn" should be printed out.
How can I elegantly solve this problem? I can restructure my hash if necessary.
def find_region(country_code)
pair = #region_map.find{|k, v| v.include?(country_code)}
pair && pair.first
end
find_region('gb') # => "gb"
find_region('bz') # => "ar"
find_region('lv') # => "ee"
find_region('ls') # => nil
def find_region(country_code)
#region_map.each {|k,v| return k if v.include? country_code}
nil
end
region_map = Hash.new("us").merge(
"au" => "au",
"al" => "al",
"ba" => "al",
"bg" => "al",
"hr" => "al",
"md" => "al",
"me" => "al",
"mk" => "al",
"ro" => "al",
"si" => "al",
"al" => "al",
"cn" => "cn",
...
)
region_map["non existing code"] # => "us"
region_map[nil] # => "us"
region_map["au"] # => "au"
region_map["ba"] # => "al"
region_map["cn"] # => "cn"
or
def region_map code
case code
when "au"
"au"
when "al", "ba", "bg", "hr", "md", "me", "mk", "ro", "si"
"al"
when "cn"
"cn"
when "ee", "lt", "lv"
"ee"
...
else
"us"
end
end
region_map("non existing code") # => "us"
region_map(nil) # => "us"
region_map("au") # => "au"
region_map("ba") # => "al"
region_map("cn") # => "cn"

Deleting duplicate data in ruby

I want to remove duplicates from my code based on all the three fields i.e based on event-name,date and time,if all the three fields match for two events then one should be removed showing only unique events.The code I have written works for only for one field i.e its removing entries based on event-name.How do i match all the three fields. I was using & but that didn't work out.
include Enumerable
list = [
{
:eventname => "music show",
:date => "1st august",
:time => "9 pm"
},
{
:eventname => "dance show",
:date => "11st august",
:time => "9 pm"
},
{
:eventname => "music show",
:date => "1st august",
:time => "9 pm"
},
{
:eventname => "music show",
:date => "15st august",
:time => "9 pm"
},
{
:eventname => "magic show",
:date => "12st august",
:time => "9 pm"
},
{
:eventname => "rock show",
:date => "1st august",
:time => "9 pm"
}
]
b=list.group_by{|r| r[:eventname]}.map do |k, v|
v.inject({}) { |r, h| r.merge(h){ |key, o, n| o || n } }
end
Use the uniq mthod of Array:
list.uniq

Resources