Merge duplicated values in array of hashes - ruby

I have an array of hashes like this
arr_of_hashes = [
{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
]
I want to remove duplicates. Also, among:
{"started_at"=>"2018-07-16", "stopped_at"=>"2018-07-16"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
I want to keep only the last line. How can I do that?
I tried to do:
sorted_arr = arr_of_hashes.uniq

arr_of_hashes.reverse.uniq { |hash| hash["started_at"] }.reverse
About pass block to uniq and about reverse.
#result
[
{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}
]

Something like this?
[2] pry(main)> arr_of_hashes.reject { |h| h['started_at'] == h['stopped_at'] }
[
[0] {
"started_at" => "2018-07-16",
"stopped_at" => "still active"
}
]
Its not clear form your question what output you want to get

arr_of_hashes.each_with_object({}) { |g,h| h.update(g["started_at"]=>g) }.values
#=> [{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
# {"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
# {"started_at"=>"2018-07-16", "stopped_at"=>"still active"}]
See Hash#update (a.k.a. merge!) and note that values's receiver is as follows.
arr_of_hashes.each_with_object({}) { |g,h| h.update(g["started_at"]=>g) }
#=> {"2018-07-11"=>{"started_at"=>"2018-07-11", "stopped_at"=>"2018-07-11"},
# "2018-07-13"=>{"started_at"=>"2018-07-13", "stopped_at"=>"2018-07-13"},
# "2018-07-16"=>{"started_at"=>"2018-07-16", "stopped_at"=>"still active"}}

Related

Modifying an Array

I have an Array like this
example_array = ['dog', 'cat', 'snake']
And I am trying to append the timestamp to each element of the array and the output should look like
example_array = [{'dog': 'time_stamp'},{'cat':'time_stamp'},{'snake':'time_stamp'}]
I've tried this but the output is incorrect:
a = {}
example_array.each_with_index do |element, i|
a.merge!("#{element}": "#{Time.now}")
example_array.delete_at(i)
end
Can anyone suggest me a solution in ruby?
I have tried a lot of ways but couldn't obtain the output like above.
Aditha,
How about this?
array = ["cat", "hat", "bat", "mat"]
hash = []
hash.push(Hash[array.collect { |item| [item, Time.now] } ])
OUTPUT: => [{"cat"=>"2018-02-28 04:23:08 UTC", "hat"=>"2018-02-28 04:23:08 UTC", "bat"=>"2018-02-28 04:23:08 UTC", "mat"=>"2018-02-28 04:23:08 UTC"}]
Instead of item.upcase you would insert your timestamp info. It gives me hashes inside of array.
example_array.product([Time.now]).map { |k,v| { k.to_sym=>v }}
#=> [{:dog=>2018-02-27 20:42:56 -0800},
# {:cat=>2018-02-27 20:42:56 -0800},
# {:snake=>2018-02-27 20:42:56 -0800}
]Note this ensures that all values (timestamps) are equal.
only weird thing is that you have to use => instead of :
arr = ['dog', 'cat', 'snake']
arr2 = []
for index in 0 ... arr.size
arr2.push({arr[index] => Time.now})
end
puts arr2
['dog', 'cat', 'snake'].map{|e| [{e.to_sym => "time_stamp"}]}
# => [[{:dog=>"time_stamp"}], [{:cat=>"time_stamp"}], [{:snake=>"time_stamp"}]]

Initializing a hash with an empty array keyed to an array of strings - Ruby

I have:
people=["Bob","Fred","Sam"]
holidays = Hash.new
people.each do |person|
a=Array.new
holidays[person]=a
end
gifts = Hash.new
people.each do |person|
a=Array.new
gifts[person]=a
end
Feels clunky. I can't seem to figure a more streamline way with an initialization block or somesuch thing. Is there an idiomatic approach here?
Ideally, I'd like to keep an array like:
lists["holidays","gifts",...]
... and itterate through it to initialize each element in the lists array.
people = %w|Bob Fred Sam|
data = %w|holidays gifts|
result = data.zip(data.map { people.zip(people.map { [] }).to_h }).to_h
result['holidays']['Bob'] << Date.today
#⇒ {
# "holidays" => {
# "Bob" => [
# [0] #<Date: 2016-11-04 ((2457697j,0s,0n),+0s,2299161j)>
# ],
# "Fred" => [],
# "Sam" => []
# },
# "gifts" => {
# "Bob" => [],
# "Fred" => [],
# "Sam" => []
# }
# }
More sophisticated example would be:
result = data.map do |d|
[d, Hash.new { |h, k| h[k] = [] if people.include?(k) }]
end.to_h
The latter produces the “lazy initialized nested hashes.” It uses the Hash#new with a block constructor for nested hashes.
Play with it to see how it works.
A common way of doing that would be to use Enumerable#each_with_objrect.
holidays = people.each_with_object({}) { |p,h| h[p] = [] }
#=> {"Bob"=>[], "Fred"=>[], "Sam"=>[]}
gifts is the same.
If you only want a number of such hashes then, the following should suffice:
count_of_hashes = 4 // lists.count; 4 is chosen randomly by throwing a fair die
people = ["Bob", "Fred", "Sam"]
lists = count_of_hashes.times.map do
people.map {|person| [person, []]}.to_h
end
This code also ensures the arrays and the hashes all occupy their own memory. As can be verified by the following code:
holidays, gifts, *rest = lists
holidays["Bob"] << "Rome"
And checking the values of all the other hashes:
lists
=> [
{"Bob"=>["Rome"], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]},
{"Bob"=>[], "Fred"=>[], "Sam"=>[]}
]

method similar to array#pop for hash

I have a hash of people keyed by job and sorted by salary:
person = Struct.new(:salary)
people = {
:butchers => [
person.new(10),
person.new(6),
person.new(4)
],
:bakers => [
person.new(16),
person.new(8),
person.new(7)
],
:candlestick_makers => [
person.new(25),
person.new(21),
person.new(18)
]
}
I want to remove the last x people of each job from their respective array and do something:
def this_example_method
people.each do |job, people|
people.pop(number_for(job)).each do |person|
#do something
end
end
end
the 'do something' works okay, but pop removal doesn't. After running this_example_method, the people hash should look this, but at the moment it's not changing:
people = {
butchers = [
<butcher_1 salary:10>
<butcher_2 salary:6>
],
bakers = [
<baker_1 salary:16>
<baker_2 salary:8>
],
candlestick_makers = [
<candlestick_maker_1 salary:25>
<candlestick_maker_2 salary:21>
]
}
Hash has a shift method that returns the first item and removes it from the hash. If the order matters you could perhaps try to sort it reversed when the hash is created.
Just do as below :
def this_example_method
people.each do |job, persons|
persons.tap { |ob| ob.pop(x) }.each do |person|
#do something
end
end
end
Example :
hash = { :a => [1,2,3], :b => [3,5,7] }
hash.each do |k,v|
v.tap(&:pop).each { |i| # working wit i }
end
hash # => {:a=>[1, 2], :b=>[3, 5]}

How to map a Ruby hash?

Is there a better way to map a Ruby hash? I want to iterate over each pair and collect the values. Perhaps using tap?
hash = { a:1, b:2 }
output = hash.to_a.map do |one_pair|
k = one_pair.first
v = one_pair.last
"#{ k }=#{ v*2 }"
end
>> [
[0] "a=2",
[1] "b=4"
]
Ruby's hash includes the Enumerable module which includes the map function.
hash = {a:1, b:2}
hash.map { |k, v| "#{k}=#{v * 2}" }
Enumerable#map | RubyDocs
Err, yes, with map, invoked directly on the hash:
{ a:1, b:2 }.map { |k,v| "#{k}=#{v*2}" } # => [ 'a=2', 'b=4' ]
hash = { a:1, b:2 }
hash.map{|k,v| [k,v*2]* '='}
# => ["a=2", "b=4"]

Ruby easy search for key-value pair in an array of hashes

Suppose I have this array of hashes:
[
{"href"=>"https://company.campfirenow.com", "name"=>"Company", "id"=>123456789, "product"=>"campfire"},
{"href"=>"https://basecamp.com/123456789/api/v1", "name"=>"Company", "id"=>123456789, "product"=>"bcx"},
{"href"=>"https://company.highrisehq.com", "name"=>"Company", "id"=>123456789, "product"=>"highrise"}
]
How can I parse the "href" value of the hash where "product"=>"bcx"
Is there any easy way to do this in Ruby?
ary = [
{"href"=>"https://company.campfirenow.com", "name"=>"Company", "id"=>123456789, "product"=>"campfire"},
{"href"=>"https://basecamp.com/123456789/api/v1", "name"=>"Company", "id"=>123456789, "product"=>"bcx"},
{"href"=>"https://company.highrisehq.com", "name"=>"Company", "id"=>123456789, "product"=>"highrise"}
]
p ary.find { |h| h['product'] == 'bcx' }['href']
# => "https://basecamp.com/123456789/api/v1"
Note that this only works if the element exists. Otherwise you will be calling the subscription operator [] on nil, which will raise an exception, so you might want to check for that first:
if h = ary.find { |h| h['product'] == 'bcx' }
p h['href']
else
puts 'Not found!'
end
If you need to perform that operation multiple times, you should build yourself a data structure for faster lookup:
href_by_product = Hash[ary.map { |h| h.values_at('product', 'href') }]
p href_by_product['campfire'] # => "https://company.campfirenow.com"
p href_by_product['bcx'] # => "https://basecamp.com/123456789/api/v1"

Resources