Select named array from a hash of array of arrays - ruby

I have a hash of an array of arrays. The array is indexed by the hash (that is the way I am reading this):
[
{"name":"G18","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]},
{"name":"G19","data": [["M16",141],["M203",57],["M29S",142]]},
{"name":"G20","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]}
]
I want to select the hashes that contain the array G18, and return only the data.
I tried searching for answer, but I haven't found anything like this yet.

This will work for you if you have only one item with name "G18":
a.find {|e| e[:name] == "G18" }[:data]
See: Enumerable#find in the official docs.

Given:
ary = [
{"name":"G18","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]},
{"name":"G19","data": [["M16",141],["M203",57],["M29S",142]]},
{"name":"G20","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]}
]
Try:
ary.select{|hsh| hsh[:name] == 'G18'}.first[:data]
=> [["X301", 141], ["x7901", 57], ["x2100", 142], ["x90", 58]]
In fact, marmeladze's answer is the correct one:
ary.find{|hsh| hsh[:name] == 'G18'}[:data]
Using select was a misfire.

collection = [
{"name":"G18","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]},
{"name":"G19","data": [["M16",141],["M203",57],["M29S",142]]},
{"name":"G20","data": [["X301",141],["x7901",57],["x2100",142],["x90",58]]}
]
def data_for_name(name, collection)
collection.find { |item| item[:name] == name }[:data]
end
p data_for_name("G18", collection)

Related

Print particular values from object based on comparison

I am working on developing a UI wherein I need to retrieve particular values from an object based on conditions. object looks like below.
Category = {["personal_care_appliances", "glwise_category_config", "{catWhitelist: []}"],
["wine","glwise_category_config","{}"],
["shoes","glwise_category_config","{catWhitelist: []}"],
["automotive","glwise_category_config",],
["watch","glwise_category_config","{catWhitelist: []}"]
]
I need to print first element such as personal_care_appliances,shoes,watch as they have a component catWhitelist: [].
I tried using map and array syntax but it did not work.
PS. I am quite new to Ruby and learning through online docs.
Assuming that category is an array of arrays (which is not quite clear from your question), try map first element of nested array if the nested array includes "{catWhitelist: []}":
category.map { |e| e[0] if e.include? "{catWhitelist: []}" }.compact
# => ["personal_care_appliances", "shoes", "watch"]
Or, better to select required subarrays before to map it:
category.select { |e| e.include? "{catWhitelist: []}" }.map{ |e| e[0] }
If we assume you have this array:
category = [
["personal_care_appliances", "glwise_category_config", "{catWhitelist: []}"],
["wine","glwise_category_config","{}"],
["shoes","glwise_category_config","{catWhitelist: []}"],
["automotive","glwise_category_config",],
["watch","glwise_category_config","{catWhitelist: []}"]
]
and you want to get only the items which have catWhitelist, then you can use a method like:
def with_cat_whitelist(category)
result = []
category.each do |item|
result << item.first if item.last == "{catWhitelist: []}"
end
result
end

Ruby, How can I compare two pair of items in array of hashes

I have a array of hashes like :
arry = {hash1, hash2, hash3,hash4 ....hash_N}
for each hash,
hash1 ={ "deviceId"=>"868403021230682", "osVersion"=>"6.0", "deviceOS"=>"Android",
"appVersion"=>"1.7.2", "action"=>"Enter"}
hash2 = { "deviceId"=>"868403021230682", "osVersion"=>"6.0", "deviceOS"=>"Android",
"appVersion"=>"1.7.2", "action"=>"Leave"}
because it is possible that for each hash "action"=>"Enter" or "Leave" will not always appear as a pair , for example, action for hash3, hash4,hash5 could be all "Enter" . My idea is only consider two hashes who can make a pair like hash1 and hash2, remove other from array or put them into other array.
so the new array should just contain [hash1, hash2, hash7,hash8] , lets say hash7 and 8 are also a pair.
should I use each_with_index? my code is like this:
def get_result(doc)
result = []
doc.each_slice(2).map { |pair|
pair.each_with_index { |element, index|
if ( pair[index].has_value?([:action] => "enter") &&pair[index+1].has_value?([:action] => "Leave")
result.push(pair)
end
}
}
end
but the if statement is not working right , kind of confused about how to use each_with_index hope someone can help me out
Based on the way your method is created , you can do it this way:
def get_result(doc)
doc.each_sli­ce(2).to_a­.map{ |ah| ah if ah[0][:action] == 'Enter'­ && ah[1]­[:action] == 'Leave'­}.compact.flatten
end
Explanation
The variable doc is an array of hashes [hash1, hash2, ...] when we create doc.each_slice(2).to_a will return an array of pair of hashes [[hash1, hash2],[hash3,hash4]...], Now when we do map and get the pair of hashes that has actions per order ('Enter','Leave') we get an array with nil values like this [[hash1,hash2],nil,[hash5,hash6]..] . we use compact to remove the nil values. now the array is like this [[hash1,hash2],[hash5,hash6]..] (array of pair of hashes) and the result expected is an array of hashes, that's why we need flatten, it will remove the inside array and return an array like this [hash1, hash2, hash5, hash6 ...]
If you need to get the list of deleted hashes, i think it would be better if add another method to do it. Otherwise, you can make the get_result method return two arrays.
Here is how you can do it :
def get_result(doc)
removed_elms = []
result = doc.each_sli­ce(2).to_a­.map do |ah|
# if you just need them different (not the first one 'Enter' and the second one 'Leave') you need to set the commented condition
# if ['Enter','Leave'].include?(ah[0][:action] && ah[1][:action]) && ah[0][:action] != ah[1][:action]
if ah[0][:action] == 'Enter'­ && ah[1]­[:action] == 'Leave'
ah
else
removed_elms << ah
nil
end
end.compact
[result.flatten, removed_elms.flatten]
end

Find first value in hash array

I have the following array.
[
{"title"=>"un", "link"=>nil, "description"=>""},
{"title"=>"deux", "link"=>"https://apple.com", "description"=>"Products"},
{"title"=>"three", "link"=>"http://www.amazon.com", "description"=>"Welcome"},
{"title"=>"four", "link"=>"https://apple.com", "description"=>"iPad"},
]
I'd like to remove hashes whose link is nil or empty, an return an array of links to get:
["https://apple.com", "http://amazon"]
Not sure how to remove hashes and get unique links only.
To remove both empty ("") and nil values:
arr.map { |a| a["link"] }.reject(&:blank?).uniq
As #Arup pointed out blank is in Rails, pure Ruby solution would be:
arr.map { |a| a["link"] }.reject{|e| e == "" || e == nil}.uniq
Please try like below code
arr.map {|o| o['link'] }.reject { |e| e.to_s.empty? }.uniq
Solution:
a = [
{"title"=>"un", "link"=>nil, "description"=>""},
{"title"=>"deux", "link"=>"https://apple.com", "description"=>"Products"},
{"title"=>"three", "link"=>"http://www.amazon.com", "description"=>"Welcome"},
{"title"=>"four", "link"=>"https://apple.com", "description"=>"iPad"},
]
a.map{|data| data["link"] }.reject(&:blank?).uniq
Explanation:
a is the array of hashes
Map will go through each hash in the array and create a new array with just the "link" value. If the original hash didn't have a link key it will create a nil value in the new array.
Example result after map:
[nil, "https://apple.com", "https://www.amazon.com", "https://apple.com"]
Reject then calls the rails active support method blank? on each item in the array and if blank? returns true returns a new array without the value:
[nil.blank?, "https://apple.com".blank?, "https://www.amazon.com".blank?, "https://apple.com".blank?]
Result:
["https://apple.com", "https://www.amazon.com", "https://apple.com"]
Uniq then generates an array without duplicates (an alternative is to use a ruby set)
["https://apple.com", "https://www.amazon.com"]
You could do by:
arr.map {|o| o['link'] }.compact.uniq
compact remove all nils, and uniq returns the unique values.
You could go with Enumerable#each_with_object :
array.each_with_object([]) do |h, a|
a << h['link'] unless h['link'].nil? || h['link'].empty?
end.uniq
# => ["https://apple.com", "http://www.amazon.com"]

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

I have this array of hashes:
results = [
{"day"=>"2012-08-15", "name"=>"John", "calls"=>"5"},
{"day"=>"2012-08-15", "name"=>"Bill", "calls"=>"8"},
{"day"=>"2012-08-16", "name"=>"Bill", "calls"=>"11"},
]
How can I search the results to find how many calls Bill made on the 15th?
After reading the answers to "Ruby easy search for key-value pair in an array of hashes", I think it might involve expanding upon the following find statement:
results.find { |h| h['day'] == '2012-08-15' }['calls']
You're on the right track!
results.find {|i| i["day"] == "2012-08-15" and i["name"] == "Bill"}["calls"]
# => "8"
results.select { |h| h['day'] == '2012-08-15' && h['name'] == 'Bill' }
.reduce(0) { |res,h| res += h['calls'].to_i } #=> 8
A Really clumsy implementation ;)
def get_calls(hash,name,date)
hash.map{|result| result['calls'].to_i if result['day'] == date && result["name"] == name}.compact.reduce(:+)
end
date = "2012-08-15"
name = "Bill"
puts get_calls(results, name, date)
=> 8
Or another possible way, but a little worse, using inject:
results.inject(0) { |number_of_calls, arr_element| arr_element['day'] == '2012-08-15' ? number_of_calls += 1 : number_of_calls += 0 }
Note that you have to set number_of_calls in each iteration, otherwise it will not work, for example this does NOT work:
p results.inject(0) { |number_of_calls, arr_element| number_of_calls += 1 if arr_element['day'] == '2012-08-15'}
Actually, "reduce" or "inject" is specifically for this exact operation (To reduce the contents of an enumerable down into a single value:
results.reduce(0) do |count, value|
count + ( value["name"]=="Bill" && value["day"] == "2012-08-15" ? value["calls"].to_i : 0)
end
Nice writeup here:
"Understanding map and reduce"

Determine if a value exists in an array of hashes

I have the following:
array_of_hashes = [{:a=>10, :b=>20}, {:a=>11, :b=>21}, {:a=>13, :b=>23}]
How would I go about finding if :a=>11 exists in array_of_hashes
array_of_hashes.include? does not seem to work
array_of_hashes.any? {|h| h[:a] == 11}
You did ask for a boolean result in the OQ, but if you really want the hash element itself do:
array_of_hashes.detect { |h| h[:a] == 11 }
If you want the result really fast you could group the original object and then get the result with a single hash lookup:
t = array_of_hashes.group_by { |x| x[:a] }
t[11]

Resources