I have an array of hashes, e.g.:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=nil}
I want to give them all ID values based on a given search criteria. For example, searching by Size should return:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"=nil, "ID"="0"}
I have written the following code which checks each hash in the sequence with the following hashes.
for i in 0..data.length-2
data[i].store("ID", i)
for j in i 1..data.length-1
output = (data[i].keys & data[j].keys).select { |k| data[i][k] == data[j][k] }
if output.include? searchTerm
puts "Match!"
puts "---"
data[j].store("ID", data[i]["ID"])
else
puts "No match :("
puts "---"
end
end
puts "---Finished checking row---"
end
puts data
The issue is twofold:
A. Nil values count as a match, e.g when searching by Colour:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"=nil, "ID"="1"}
B. Matches seem to only work for the last pair found, e.g. when searching by Size:
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="0"}
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=nil, "ID"="1"}
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"="Brown", "ID"="2"}
{"Breed"=>"Poodle", "Size"=>"Medium", "Colour"="White", "ID"="2"}
In summary, I want to ignore nil values so they don't count as matches and for all instances of the same key:value pair to have the same value for the ID key.
CodePudding user response:
Don't use for loops for array iteration. Ruby has plenty of nice methods for doing that.
Here is my solution:
hashes = [{"Breed" => "Beagle", "Size" => "Medium", "Colour" => "Brown"},
{"Breed" => "Pug", "Size" => "Small", "Colour" => nil},
{"Breed" => "Beagle", "Size" => "Medium", "Colour" => "Brown"},
{"Breed" => "Beagle", "Size" => "Large", "Colour" => nil}]
def search_id(elements, search_key)
# Get rid of elements with nil values.
target_elements = elements.reject {|e| e[search_key].nil?}
resul = []
id = 0
# Iterate through target_elements
target_elements.each do |currentElement|
# Check if exsits an element with the same value in the resul array
match = resul.find {|previousElement| currentElement[search_key] == previousElement[search_key]}
if match
# Use previous id
currentElement[:id] = match[:id]
else
# Assing a new id
currentElement[:id] = id
id = 1
end
# Add element to result
resul << currentElement
end
resul
end
puts search_id(hashes, 'Size')
CodePudding user response:
I think that it might be worth considering grabbing all of the unique values from the searched entries first and then assigning each of them an id inside a hash (with the available terms as the keys), so that you can simply loop though the hashes and grab the correct id from the pre-generated hash. e.g. something like:
hashes = [
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown"},
{"Breed"=>"Pug", "Size"=>"Small", "Colour"=>nil},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>"Brown"},
{"Breed"=>"Beagle", "Size"=>"Medium", "Colour"=>nil}
]
def append_id(items, search_key)
id_map = items.
map { |hash| hash[search_key] }.
uniq.
reject { |value| value.nil? }.
map.with_index { |value, index| [value, index] }.
to_h
items.each do |hash|
value = hash[search_key]
hash['ID'] = id_map[value] unless value.nil?
end
items
end
append_id(hashes, 'Size')