How do I create a hash in Ruby that compares strings, ignoring case? How do I create a hash in Ruby that compares strings, ignoring case? ruby ruby

How do I create a hash in Ruby that compares strings, ignoring case?


To prevent this change from completely breaking independent parts of your program (such as other ruby gems you are using), make a separate class for your insensitive hash.

class HashClod < Hash  def [](key)    super _insensitive(key)  end  def []=(key, value)    super _insensitive(key), value  end  # Keeping it DRY.  protected  def _insensitive(key)    key.respond_to?(:upcase) ? key.upcase : key  endendyou_insensitive = HashClod.newyou_insensitive['clod'] = 1puts you_insensitive['cLoD']  # => 1you_insensitive['CLod'] = 5puts you_insensitive['clod']  # => 5

After overriding the assignment and retrieval functions, it's pretty much cake. Creating a full replacement for Hash would require being more meticulous about handling the aliases and other functions (for example, #has_key? and #store) needed for a complete implementation. The pattern above can easily be extended to all these related methods.


If you really want to ignore case in both directions and handle all Hash methods like #has_key?, #fetch, #values_at, #delete, etc. , you'll need to do a little work if you want to build this from scratch, but if you create a new class that extends from class ActiveSupport::HashWithIndifferentAccess, you should be able to do it pretty easily like so:

require "active_support/hash_with_indifferent_access"class CaseInsensitiveHash < HashWithIndifferentAccess  # This method shouldn't need an override, but my tests say otherwise.  def [](key)    super convert_key(key)  end  protected  def convert_key(key)    key.respond_to?(:downcase) ? key.downcase : key  end  end

Here's some example behavior:

h = CaseInsensitiveHash.newh["HELLO"] = 7h.fetch("HELLO")                # => 7h.fetch("hello")                # => 7h["HELLO"]                      # => 7h["hello"]                      # => 7h.has_key?("hello")             # => trueh.values_at("hello", "HELLO")   # => [7, 7]h.delete("hello")               # => 7h["HELLO"]                      # => nil


Any reason for not just using string#upcase?

h = Hash.newh["HELLO"] = 7puts h["hello".upcase]

If you insist on modifying hash, you can do something like the following

class Hashalias :oldIndexer :[]def [](val)   if val.respond_to? :upcase then oldIndexer(val.upcase) else oldIndexer(val) endendend

Since it was brought up, you can also do this to make setting case insensitive:

class Hashalias :oldSetter :[]=def []=(key, value)    if key.respond_to? :upcase then oldSetter(key.upcase, value) else oldSetter(key, value) endendend

I also recommend doing this using module_eval.