Extending Hast class with custom = methods

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?

Hi –

On Wed, 23 Apr 2008, Iñaki Baz C. wrote:


}

How could I do it?

You can reopen the Hash class in Ruby, but it’s not a good idea to
change the documented behavior of core methods. A better way is to
write a module, and then use it selectively for the hashes that need
it:

module CaseInsensitiveLookup
def

etc., and then:

my_hash.extend(CaseInsensitiveLookup)

David

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

Yes, but my question is what to do into that:
def " 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.

Hi,

You can use super and/or alias:

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

Dan

On Apr 22, 6:15 pm, Daniel F. [email protected] 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
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

El Miércoles, 23 de Abril de 2008, Daniel F.
escribió:> Dan
opss, yes, it was no so difficult XDD
Thanks a lot.

On Apr 22, 7:31 pm, Chris S. [email protected] wrote:

end
end

Chris

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

!!!
Chris

2008/4/23, Chris S. [email protected]:

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

2008/4/23, Robert K. [email protected]:

2008/4/23, Chris S. [email protected]:

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
getobj[(k.downcase rescue k)]
end

add other lookup and mutation methods

end

2008/4/23, Iñaki Baz C. [email protected]:

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

It works. :slight_smile:

… 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 = 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

Cheers

robert

2008/4/23, Chris S. [email protected]:

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. :slight_smile:

2008/4/23, Robert K. [email protected]:

… 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 :slight_smile:
Thanks.