【问题标题】:How do I create a hash in Ruby that compares strings, ignoring case?如何在 Ruby 中创建一个比较字符串的哈希,忽略大小写?
【发布时间】:2011-01-03 01:45:21
【问题描述】:

在 Ruby 中,我想在 Hash 中存储一些东西,但我不希望它区分大小写。比如:

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

这应该输出 7,即使大小写不同。我可以只覆盖哈希的相等方法或类似的方法吗?

谢谢。

【问题讨论】:

    标签: ruby hash key


    【解决方案1】:

    有什么理由不只使用 string#upcase?

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

    如果你坚持要修改hash,你可以这样做

    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。

    【讨论】:

    • 假设设置散列是用全部大写完成的。您可以轻松地将上述内容扩展到 []=
    【解决方案2】:

    总的来说,我会说这是一个糟糕的计划;但是,如果我是你,我会创建一个覆盖 [] 方法的哈希子类:

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

    【讨论】:

    • 只是为这个答案添加更多内容:覆盖 #[]= 以在收到的键上调用 #downcase,然后在 #[] 中调用 self.get(search.downcase)。
    • @Federico Builes - +1 - 感谢您的额外帮助 :-)
    【解决方案3】:
    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
    

    【讨论】:

      【解决方案4】:

      如果你真的想在两个方向上忽略大小写并处理所有 Hash 方法,如 #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
      

      【讨论】:

      • 不错,我不知道!
      • 警告,这种方法不适用于嵌套哈希,因为您的子类不会被使用
      【解决方案5】:

      为防止此更改完全破坏程序的独立部分(例如您正在使用的其他 ruby​​ gem),请为您的不敏感哈希创建一个单独的类。

      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)。上面的模式可以很容易地扩展到所有这些相关的方法。

      【讨论】:

      • 我意识到这是将近五年前的事了,但“你这个麻木不仁的笨蛋”让我大笑起来。太棒了。
      • 警告:这也不适用于嵌套哈希。
      • @James 据我所知,如果您将每个嵌套级别创建为 HashClod。如果您创建默认哈希当然会失败。
      【解决方案6】:

      虽然 Ryan McGeary 的方法效果很好,而且几乎可以肯定是正确的方法,但有一个我无法找出原因的错误,它破坏了 Hash[] 方法。

      例如:

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

      虽然我无法找到或修复错误的根本原因,但以下 hack 确实可以改善问题:

      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}
      

      【讨论】:

      • 作为后续,我真正需要的是忽略大小写/保留大小写的哈希,但子类化 HashWithIndifferentAccess 最终会修改原始键的大小写(更不用说上面的 [] 方法错误) ),所以我放弃了,而是在 Hash 上修改了一个 fetch_indifferently 方法。
      猜你喜欢
      • 1970-01-01
      • 2016-01-29
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 2016-02-23
      • 1970-01-01
      • 2011-02-20
      • 2023-04-10
      相关资源
      最近更新 更多