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?

--
Iñaki Baz Castillo

Hi --

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?

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 (key)

etc., and then:

   my_hash.extend(CaseInsensitiveLookup)

David

···

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

--
Rails training from David A. Black and Ruby Power and Light:
   INTRO TO RAILS June 9-12 Berlin
   ADVANCING WITH RAILS June 16-19 Berlin
   INTRO TO RAILS June 24-27 London (Skills Matter)
See http://www.rubypal.com for details and updates!

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.

···

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)

--
Iñaki Baz Castillo

Hi,

You can use super and/or alias:

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

Dan

···

On Tue, Apr 22, 2008 at 8:01 PM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

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

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

···

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

--
Iñaki Baz Castillo

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:

···

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

###
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

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

!!!
Chris

···

On Apr 22, 7:31 pm, Chris Shea <cms...@gmail.com> wrote:

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

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, Chris Shea <cmshea@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:

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

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, Chris Shea <cmshea@gmail.com>:

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

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

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

Cheers

robert

···

2008/4/23, Iñaki Baz Castillo <ibc@aliax.net>:

2008/4/23, Chris Shea <cmshea@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. :slight_smile:

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

> 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

···

2008/4/23, Robert Klemme <shortcutter@googlemail.com>:

2008/4/23, Chris Shea <cmshea@gmail.com>:

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

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

···

2008/4/23, Robert Klemme <shortcutter@googlemail.com>:

... and is awfully inefficient.

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

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