Is there anything like boost::multi_index but for ruby. Basically taking some container of objects and having it indexed N different ways with N different query methods.
I guess you could use DataMapper with the SQLite in memory database but I was wondering if there is anything pure ruby around.
Below is an imagined example of what this type of class might do. It looks very much like a database.
class Foo
attr_accessor :a
attr_accessor :b
attr_accessor :c
end
class FooIndexer < MultiIndex
hash_index :a do |o|
o.a
end
ordered_index :b do |x, y|
x.b <=> y.b
end
end
index = FooIndexer.new
index.insert( Foo.new ( ... ))
index.insert( Foo.new ( ... ))
index.insert( Foo.new ( ... ))
index.insert( Foo.new ( ... ))
index.insert( Foo.new ( ... ))
index.find ( index.a == 10 )
index.find ( index.b > 10 )
This is a fully worked solution including spec but only for multiple hash keys.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With