hi all,
ok here’s the scoop. i have a collection of hashes all with an arbitrary
number of symbolic keys pointing to arbitrary objects. i have a template
hash also containing symbol keys pointing to ‘matching objects’, i.e.
objects to be matched with the objects in the aforementioned collection,
and i want to pull out the first match. an example:
please don’t psychoanalyze the below
collection = [
{ :foo => ‘python’, :bar => ‘php’, :x => 69 },
{ :foo => ‘scheme’, :mama => ‘ruby’, :papa => ‘C’ },
{ :x => 386 },
{ :mama => ‘ruby’, :papa => ‘simula’, :lil_bro => ‘fortran’ }
]
template1 = { :mama => ‘ruby’, :papa => ‘C’ }
template2 = { :x => Numeric }
match_first(template1, collection)
should return: { :foo => ‘scheme’, :mama => ‘ruby’, :papa => ‘C’ }
match_all(template2, collection)
should return: [{:x => 386 }, {:foo => ‘python’, :bar => ‘php’, :x =>
69}]
so obviously === is being used internally as is evidenced by Numeric in
template2, but what kind of datastructures/algorithms should i be
looking at for the most efficient (time-wise) implementation, keeping in
mind, there may be many hashes in the collection and those hashes are
generally not very large, i.e. contain few key/value pairs. i’m not too
savy on the taxonomy of algorithms so any pointers to what i should be
looking at to read up on would be appreciated.
my potentially naive solution is something like the following (in full
color Parked at Loopia):
require ‘set’
@collection = {}
def add hash
hash.each do |key,value|
(@collection[key] ||= Set.new) << hash
end
end
def candidates template
sets = []
template.keys.each do |key|
sets << @collection[key] if @collection.include? key
end
if sets.empty?
return sets
else
return sets.inject(sets.pop) do |intersection, set|
intersection & set
end
end
end
def match template, hash
template.each do |key, match|
return false unless match === hash[key]
end
return true
end
def match_first template
candidates(template).each do |hash|
return hash if match(template, hash)
end
return nil
end
def match_all template
return candidates(template).select do |hash|
match(template, hash)
end
end
[
{ :foo => ‘python’, :bar => ‘php’, :x => 69 },
{ :foo => ‘scheme’, :mama => ‘ruby’, :papa => ‘C’ },
{ :x => 386, :papa => ‘clause’ },
{ :mama => ‘ruby’, :papa => ‘simula’, :lil_bro => ‘fortran’ },
{ :mama => 1946, :boo => ‘far’, :x => ‘x’}
].each do |hash|
add hash
end
puts match_first({ :mama => ‘ruby’, :papa => ‘C’ }).inspect
puts match_all({:foo => String}).inspect
thanks for reading this far!
_c