2010-01-08 42 views

Trả lời

11

Để ngăn chặn sự thay đổi này từ các bộ phận độc lập hoàn toàn phá vỡ chương trình của bạn (chẳng hạn như đá quý ruby ​​khác mà bạn đang sử dụng), tạo ra một lớp học riêng cho băm nhạy cảm của bạn.

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 

Sau khi ghi đè các chức năng chuyển nhượng và truy xuất, có khá nhiều bánh. Việc tạo một thay thế đầy đủ cho Hash sẽ đòi hỏi phải tỉ mỉ hơn về việc xử lý các bí danh và các hàm khác (ví dụ, #has_key? Và #store) cần thiết để thực hiện đầy đủ. Các mô hình trên có thể dễ dàng được mở rộng cho tất cả các phương pháp liên quan.

+3

Tôi nhận ra điều này đã được gần năm năm trước, nhưng 'bạn không nhạy cảm' khiến tôi cười to. Bravo. –

+0

Cảnh báo: Điều này không hoạt động với băm lồng nhau. – jrg

+0

@James Theo như tôi biết nó sẽ làm gì nếu bạn tạo mỗi cấp làm tổ như một HashClod. Nếu bạn tạo băm mặc định tất nhiên nó sẽ thất bại. –

1

Bất kỳ lý do gì để không chỉ sử dụng chuỗi # upcase?

h = Hash.new 

h["HELLO"] = 7 

puts h["hello".upcase] 

Nếu bạn nhấn mạnh vào sửa đổi băm, bạn có thể làm điều gì đó như sau

class Hash 
alias :oldIndexer :[] 

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

Vì nó đã được đưa lên, bạn cũng có thể làm điều này để làm cho thiết lập trường hợp nhạy cảm:

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

Tôi cũng khuyên bạn nên làm điều này bằng cách sử dụng module_eval.

+0

h [ "hello "] = 7? –

+0

Giả định đã được thực hiện rằng thiết lập băm đã được thực hiện với tất cả các mũ. Bạn có thể dễ dàng mở rộng ở trên thành [] = – mletterle

1

Nói chung, tôi sẽ nói rằng đây là một kế hoạch xấu; Tuy nhiên, nếu tôi là bạn, tôi muốn tạo một lớp con của băm đó sẽ ghi đè các [] phương pháp:

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

Chỉ cần thêm một chút nữa vào câu trả lời này: Ghi đè # [] = để gọi #downcase trên các phím bạn nhận được và sau đó trong # [] bạn có thể gọi self.get (tìm kiếm. . –

+0

@Federico Builes - +1 - Cảm ơn bạn đã thêm một chút :-) –

1
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 
+1

Điều này, tất nhiên, sẽ nổ tung nếu khóa không thực hiện một phương thức có tên là ... – mletterle

14

Nếu bạn thực sự muốn bỏ qua trường hợp theo cả hai hướng và xử lý tất cả các phương pháp Hash như #has_key?, #fetch , #values_at, #delete, v.v., bạn sẽ cần phải làm một công việc nhỏ nếu bạn muốn xây dựng điều này từ đầu, nhưng nếu bạn tạo một lớp mới kéo dài từ lớp ActiveSupport::HashWithIndifferentAccess, bạn sẽ có thể làm điều đó khá dễ dàng như vậy :

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 

Dưới đây là một số ví dụ hành vi:

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 
+1

Gọn gàng, tôi không biết về điều đó! –

+3

Cảnh báo, cách tiếp cận này không hoạt động với băm lồng nhau, vì lớp con của bạn sẽ không được sử dụng –

0

Trong khi phương pháp tiếp cận của Ryan McGeary hoạt động tốt và gần như chắc chắn là cách chính xác để làm điều đó, có một lỗi mà tôi đã không thể phân biệt nguyên nhân gây ra phương pháp Hash[].

Ví dụ:

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

Mặc dù tôi đã không thể tìm thấy hoặc sửa chữa các nguyên nhân cơ bản của lỗi này, hack sau không cải thiện vấn đề:

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} 
+0

Theo dõi, những gì tôi thực sự cần là một trường hợp bỏ qua/băm bảo quản trường hợp nhưng phân lớp HashWithIndifferentAccess kết thúc bằng cách nhấn vào trường hợp của các khóa gốc (chưa kể lỗi phương thức [] ở trên), vì vậy tôi đã từ bỏ và monkeypatched một phương thức fetch_indifferently trên Hash thay vào đó. – rantler

Các vấn đề liên quan