Подтвердить что ты не робот

Как создать хэш в Ruby, который сравнивает строки, игнорируя регистр?

В Ruby я хочу хранить некоторые вещи в Hash, но я не хочу, чтобы это зависало от регистра. Так, например:

h = Hash.new
h["HELLO"] = 7
puts h["hello"]

Это должно выводить 7, хотя случай отличается. Могу ли я просто переопределить метод равенства хэша или что-то подобное?

Спасибо.

4b9b3361

Ответ 1

Чтобы это изменение не полностью разрушало независимые части вашей программы (например, другие рубиновые камни, которые вы используете), создайте отдельный класс для вашего нечувствительного хэша.

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

you_insensitive = HashClod.new

you_insensitive['clod'] = 1
puts you_insensitive['cLoD']  # => 1

you_insensitive['CLod'] = 5
puts you_insensitive['clod']  # => 5

После переопределения функций назначения и извлечения, это в значительной степени торт. Создание полной замены для Hash потребует более тщательной обработки псевдонимов и других функций (например, #has_key? И #store), необходимых для полной реализации. Вышеприведенный шаблон можно легко распространить на все эти связанные методы.

Ответ 2

Если вы действительно хотите игнорировать регистр в обоих направлениях и обрабатывать все методы хэша, такие как #has_key?, #fetch, #values_at, #delete и т.д., вам нужно сделать небольшую работу, если хотите для создания этого с нуля, но если вы создадите новый класс, который простирается от класса ActiveSupport:: HashWithIndifferentAccess, вы сможете сделать это довольно легко так:

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

Вот пример поведения:

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

Ответ 3

Любая причина не просто использовать строку # upcase?

h = Hash.new

h["HELLO"] = 7

puts h["hello".upcase]

Если вы настаиваете на изменении хэша, вы можете сделать что-то вроде следующего

class Hash
alias :oldIndexer :[]

def [](val)
   if val.respond_to? :upcase then oldIndexer(val.upcase) else oldIndexer(val) end
end
end

Поскольку он был поднят, вы также можете сделать это, чтобы сделать регистр нечувствительным:

class Hash
alias :oldSetter :[]=
def []=(key, value)
    if key.respond_to? :upcase then oldSetter(key.upcase, value) else oldSetter(key, value) end
end
end

Я также рекомендую делать это с помощью module_eval.

Ответ 4

В общем, я бы сказал, что это плохой план; однако, если бы я был вами, я бы создал подкласс хэш, который переопределяет метод []:

class SpecialHash < Hash
  def [](search)
    # Do special code here
  end
end

Ответ 5

require 'test/unit'
class TestCaseIndifferentHash < Test::Unit::TestCase
  def test_that_the_hash_matches_keys_case_indifferent
    def (hsh = {}).[](key) super(key.upcase) end

    hsh['HELLO'] = 7
    assert_equal 7, hsh['hello']
  end
end

Ответ 6

В то время как подход Райана МакГери работает отлично и почти наверняка является правильным способом сделать это, есть ошибка, из-за которой я не смог распознать причину, которая нарушает метод Hash[].

Например:

r = CaseInsensitiveHash['ASDF', 1, 'QWER', 2]
=> {"ASDF"=>1, "QWER"=>2}
r['ASDF']
=> nil
ap r
{
    "ASDF" => nil,
    "QWER" => nil
}
=> {"ASDF"=>1, "QWER"=>2}

Хотя мне не удалось найти или исправить основную причину ошибки, следующий хак облегчит проблему:

r = CaseInsensitiveHash.new(Hash['ASDF', 1, 'QWER', 2])
=> {"asdf"=>1, "qwer"=>2}
r['ASDF']
=> 1
ap r
{
    "asdf" => 1,
    "qwer" => 2
}
=> {"asdf"=>1, "qwer"=>2}