Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Extending Hast class with custom [] []= methods

146 views
Skip to first unread message

Iñaki Baz Castillo

unread,
Apr 22, 2008, 7:30:34 PM4/22/08
to
Hi, I'd like to extend Hash class [] and []= methods in order to find a key
with case insensitive. This is:


- The actual Hash [] behaviour:

{"a"=>1,"b"=>2}["a"]
=> 1
{"a"=>1,"b"=>2}["A"]
=> nil

- The beaviour I look for:

{"a"=>1,"b"=>2}["a"]
=> 1
{"a"=>1,"b"=>2}["A"]
=> 1


But I can't modify [] method since it's Ruby core written in C:

----------------------
VALUE
rb_hash_aref(hash, key)
VALUE hash, key;
{
VALUE val;

if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
return rb_funcall(hash, id_default, 1, key);
}
return val;
}
-----------------------


How could I do it?


--
Iñaki Baz Castillo

Iñaki Baz Castillo

unread,
Apr 22, 2008, 8:01:59 PM4/22/08
to
El Miércoles, 23 de Abril de 2008, David A. Black escribió:
> A better way is to
> write a module, and then use it selectively for the hashes that need
> it:
>
>    module CaseInsensitiveLookup
>      def [](key)

Yes, but my question is what to do into that:
def [](key)" method
...
end

since the original code is written in C and I don't know which attributes
should I use to access to keys and values.

--
Iñaki Baz Castillo

Daniel Finnie

unread,
Apr 22, 2008, 8:15:37 PM4/22/08
to
Hi,

You can use super and/or alias:

class MyHash < Hash
def [] key
super(key.downcase)
end
end

Dan

Iñaki Baz Castillo

unread,
Apr 22, 2008, 9:02:06 PM4/22/08
to
El Miércoles, 23 de Abril de 2008, Daniel Finnie escribió:
> Hi,
>
> You can use super and/or alias:
>
> class MyHash < Hash
>   def [] key
>     super(key.downcase)
>   end
> end
>
> Dan

opss, yes, it was no so difficult XDD
Thanks a lot.

--
Iñaki Baz Castillo

Chris Shea

unread,
Apr 22, 2008, 9:31:07 PM4/22/08
to
On Apr 22, 6:15 pm, Daniel Finnie <d...@danfinnie.com> wrote:
> Hi,
>
> You can use super and/or alias:
>
> class MyHash < Hash
> def [] key
> super(key.downcase)
> end
> end
>

But he asking for case-insensitivity. If a key is created with
uppercase letters, you're out of luck. And if you look for a value
for a non-string key, that's no good either:

###
class MyHash < Hash
def [](key)
super(key.downcase)
end
end

h = MyHash.new
h['A'] = 'never findable'
h['A'] # => nil
h[1] # ~> undefined method `downcase' for 1:Fixnum (NoMethodError)
###

You could override []= as well (and with more care), but I wonder if a
different class with a Hash instance variable with mediated access
would be a better route.

Chris

Chris Shea

unread,
Apr 22, 2008, 10:54:39 PM4/22/08
to

Or just use Rubinius: http://pastie.textmate.org/185232

!!!
Chris

Robert Klemme

unread,
Apr 23, 2008, 3:22:59 AM4/23/08
to
2008/4/23, Chris Shea <cms...@gmail.com>:

> Or just use Rubinius: http://pastie.textmate.org/185232

Or delegation

h = CiHash.new
h["FOO"]=1
h[:not_a_string]=2 # works, too
puts h["foo"]
puts h[:not_a_string]

Cheers

robert

--
use.inject do |as, often| as.you_can - without end

Robert Klemme

unread,
Apr 23, 2008, 3:23:35 AM4/23/08
to
2008/4/23, Robert Klemme <short...@googlemail.com>:

> 2008/4/23, Chris Shea <cms...@gmail.com>:
>
> > Or just use Rubinius: http://pastie.textmate.org/185232
>
>
> Or delegation

Copy and paste error: this was missing:

require 'delegate'

class CiHash < DelegateClass(Hash)
def initialize
super({})
end

def []=(k,v)
__getobj__[(k.downcase rescue k)] = v
end

def [](k)
__getobj__[(k.downcase rescue k)]
end

# add other lookup and mutation methods
end

Iñaki Baz Castillo

unread,
Apr 23, 2008, 4:02:29 AM4/23/08
to
2008/4/23, Chris Shea <cms...@gmail.com>:

> But he asking for case-insensitivity. If a key is created with
> uppercase letters, you're out of luck. And if you look for a value
> for a non-string key, that's no good either:

Yeah, finally I've done:

class InsensitiveHash < Hash
def [](key)
find {|h| h[0] =~ /^#{key}$/i }[1]
end
end


It works. :)

--
Iñaki Baz Castillo
<i...@aliax.net>

Robert Klemme

unread,
Apr 23, 2008, 5:45:55 AM4/23/08
to
2008/4/23, Iñaki Baz Castillo <i...@aliax.net>:

> 2008/4/23, Chris Shea <cms...@gmail.com>:
>
>
> > But he asking for case-insensitivity. If a key is created with
> > uppercase letters, you're out of luck. And if you look for a value
> > for a non-string key, that's no good either:
>
>
> Yeah, finally I've done:
>
> class InsensitiveHash < Hash
> def [](key)
> find {|h| h[0] =~ /^#{key}$/i }[1]
> end
> end
>
>
> It works. :)

.. and is awfully inefficient.

Here's another way, which is more efficient for large hashes

class CiHash2 < DelegateClass(Hash)
CiString = Struct.new :string do
def to_s; string.downcase end
def hash; string.downcase.hash end
def eql?(s) string.downcase.eql? s.string.downcase end
alias == eql?
def inspect; string.inspect; end
end

def initialize
super({})
end

def []=(k,v)
k = CiString.new(k) if String === k
__getobj__[k] = v
end

def [](k)
k = CiString.new(k) if String === k
__getobj__[k]
end

# add other lookup and mutation methods
end

h = CiHash2.new
h["FOO"]=3
h["Foo"]=4
puts h["foo"]
puts h["fOo"]
p h

Iñaki Baz Castillo

unread,
Apr 23, 2008, 6:06:07 AM4/23/08
to
2008/4/23, Robert Klemme <short...@googlemail.com>:
> ... and is awfully inefficient.

>
> Here's another way, which is more efficient for large hashes

Oh, thanks, I'll spend some time investigating what your solution (or
how) does :)
Thanks.

0 new messages