这是一个完整的解决方案,包括规范,但仅适用于多个哈希键。
require 'pp'
class MKey
def initialize &bk
@block = bk
@containers = {}
end
def <<(val)
keys = @block.call(val)
keys.each do |k,v|
@containers[k] ||= {}
@containers[k][v] = val
end
end
def [](key)
k, v = key.first
@containers[k][v]
end
def delete(key)
val = self[key]
keys = @block.call(val)
keys.each do |k,v|
@containers[k].delete(v)
end
end
include Enumerable
def each
k, c = @containers.first
c.each do |k, val|
yield val
end
end
end
describe MKey do
class Foo
def initialize(a,b)
@a = a
@b = b
end
attr_accessor :a
attr_accessor :b
end
it "should insert" do
index = MKey.new do |o|
{ :a => o.a,
:b => o.b
}
end
x = Foo.new("hello", "cat")
y = Foo.new("goodbye", "code")
index << x
index << y
# Test Enumerable interface
index.find do |val|
val.a == "hello"
end.should == x
# Test multi key interface
index[:a => "hello"].should == x
index[:b => "code"].should == y
index.delete(:a => "hello")
index[:a => "hello"].should == nil
index[:b => "code"].should == y
index.delete(:b => "code")
index[:a => "hello"].should == nil
index[:b => "code"].should == nil
end
it "hash lookup should be faster than find" do
index = MKey.new do |o|
{ :a => o.a,
:b => o.b
}
end
for i in 1..10000
index << Foo.new(i, i*100)
end
t0 = timer do
index[:a => 1000]
end
t1 = timer do
index.find {|v| v.a == 10000}
end
t0.should < t1 * 100
end
end