2009-05-01 34 views
96

Tôi muốn thay thế mỗi value bằng một mã băm bằng value.some_method.Làm cách nào để thay đổi giá trị Hash?

Ví dụ, đối được đưa ra một băm đơn giản:

{"a" => "b", "c" => "d"}` 

mỗi giá trị nên .upcase d, vì vậy nó trông giống như:

{"a" => "B", "c" => "D"} 

tôi đã cố gắng #collect#map nhưng luôn luôn chỉ nhận được mảng trở lại . Có một cách thanh lịch để làm điều này?

CẬP NHẬT

Chết tiệt, tôi quên: Các băm là trong một instance variable mà không nên thay đổi. Tôi cần một băm mới với các giá trị thay đổi, nhưng không muốn xác định biến đó một cách rõ ràng và sau đó vòng lặp trên băm làm đầy nó. Một cái gì đó như:

new_hash = hash.magic{ ... } 
+0

ví dụ thứ hai trong câu trả lời của tôi trả về một băm mới. bình luận về nó nếu bạn muốn tôi mở rộng sự kỳ diệu mà #inject đang làm. – kch

Trả lời

185
my_hash.each { |k, v| my_hash[k] = v.upcase } 

hoặc, nếu bạn muốn làm điều đó không triệt tiêu, và trả về một băm mới thay vì sửa đổi my_hash:

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

phiên bản mới nhất này có thêm lợi ích mà bạn có thể chuyển đổi các phím quá.

+22

Đề xuất đầu tiên không phù hợp; sửa đổi một phần tử đếm được trong khi lặp lại dẫn đến hành vi không xác định - điều này cũng hợp lệ trong các ngôn ngữ khác. Đây là một câu trả lời từ Matz (ông trả lời một ví dụ bằng cách sử dụng một mảng, nhưng câu trả lời là chung chung): http://j.mp/tPOh2U – Marcus

+4

@Marcus Tôi đồng ý rằng nói chung sửa đổi như vậy nên tránh. Nhưng trong trường hợp này nó có thể an toàn, bởi vì sửa đổi không thay đổi các lần lặp lại trong tương lai. Bây giờ nếu một khóa khác (không phải là khóa được chuyển đến khối) đã được gán, thì sẽ có vấn đề. – Kelvin

+28

@Adam @kch 'each_with_object' là phiên bản thuận tiện hơn của' inject' khi bạn không muốn kết thúc khối bằng hàm băm: 'my_hash.each_with_object ({}) {| (k, v), h | h [k] = v.upcase} ' – Kelvin

9

Hãy thử chức năng này:

h = {"a" => "b", "c" => "d"} 
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}. 
+5

tốt, nhưng cần lưu ý rằng nó chỉ hoạt động khi j có một phương thức phá hoại hoạt động cho chính nó. Vì vậy, nó không thể xử lý trường hợp value.some_method chung. – kch

+0

Điểm tốt, và với bản cập nhật của mình, giải pháp thứ hai của bạn (không phá hủy) là tốt nhất. –

1

tôi làm điều gì đó như thế này:

new_hash = Hash [* original_hash.collect {| chìa khóa, giá trị | [Key, giá trị + 1]}. Flatten]

này cung cấp cho bạn với cơ sở vật chất để chuyển đổi khóa hoặc giá trị thông qua bất kỳ biểu hiện cũng (và nó không phá hủy, tất nhiên).

+0

Điều này cũng khá thông minh. Cảm ơn!:) –

1

Bạn có thể muốn tiến một bước xa hơn và thực hiện điều này trên băm lồng nhau. Chắc chắn điều này xảy ra một số tiền hợp lý với các dự án Rails.

Dưới đây là một số mã để đảm bảo một băm params là UTF-8:

def convert_hash hash 
    hash.inject({}) do |h,(k,v)| 
     if v.kind_of? String 
     h[k] = to_utf8(v) 
     else 
     h[k] = convert_hash(v) 
     end 
     h 
    end  
    end  

    # Iconv UTF-8 helper 
    # Converts strings into valid UTF-8 
    # 
    # @param [String] untrusted_string the string to convert to UTF-8 
    # @return [String] your string in UTF-8 
    def to_utf8 untrusted_string="" 
    ic = Iconv.new('UTF-8//IGNORE', 'UTF-8') 
    ic.iconv(untrusted_string + ' ')[0..-2] 
    end 
30

Bạn có thể thu thập các giá trị, và chuyển đổi nó từ Array Băm một lần nữa.

Như thế này:

config = Hash[ config.collect {|k,v| [k, v.upcase] } ] 
+0

Đây là ngắn gọn hơn và IMHO tốt hơn cú pháp - như @rewritten nhận xét về câu trả lời được chấp nhận – MatzFan

21

này sẽ làm điều đó:

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase } 

Trái ngược với inject lợi thế là bạn đang ở trong không cần phải trả lại băm lại bên trong khối.

+0

Tôi thích rằng phương pháp này không sửa đổi băm ban đầu. –

1

Ruby có phương pháp tap (1.8.7, 1.9.32.1.0) rất hữu ích cho những nội dung như thế này.

original_hash = { :a => 'a', :b => 'b' } 
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } } 
# => {:a=>"A", :b=>"B"} 
original_hash # => {:a=>"a", :b=>"b"} 
-1

Rails cụ thể

Trong trường hợp ai đó chỉ cần gọi to_s phương pháp để mỗi người trong số các giá trị và không được sử dụng Rails 4.2 (trong đó bao gồm transform_values phương pháp link), bạn có thể làm như sau:

original_hash = { :a => 'a', :b => BigDecimal('23.4') } 
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>} 
JSON(original_hash.to_json) 
#=> {"a"=>"a", "b"=>"23.4"} 

Lưu ý: Việc sử dụng các thư viện 'json' là requir ed.

Note 2: Điều này sẽ lần lượt phím thành chuỗi cũng

7

Có một phương pháp cho rằng trong ActiveSupport v4.2.0. Nó được gọi là transform_values và về cơ bản chỉ thực hiện một khối cho mỗi cặp khóa-giá trị.

Vì chúng đang thực hiện nó với một số each Tôi nghĩ rằng không có cách nào tốt hơn là lặp lại.

hash = {sample: 'gach'} 

result = {} 
hash.each do |key, value| 
    result[key] = do_stuff(value) 
end 
0

Nếu bạn biết rằng các giá trị là chuỗi, bạn có thể gọi phương thức replace vào chúng trong khi ở các chu kỳ: cách này, bạn sẽ thay đổi giá trị.

Altohugh điều này bị giới hạn trong trường hợp giá trị là chuỗi và do đó không trả lời đầy đủ câu hỏi, tôi nghĩ nó có thể hữu ích cho ai đó.

1
new_hash = old_hash.merge(old_hash) do |_key, value, _value| 
      value.upcase 
      end 

# old_hash = {"a" => "b", "c" => "d"} 
# new_hash = {"a" => "B", "c" => "D"} 
+0

Tính năng này có hiệu quả không? – ioquatix

28

Kể từ ruby 2.4.0 bạn có thể sử dụng bản địa Hash#transform_values phương pháp:

hash = {"a" => "b", "c" => "d"} 
new_hash = hash.transform_values(&:upcase) 
# => {"a" => "B", "c" => "D"} 

Ngoài ra còn có phiên bản phá hoại Hash#transform_values!.

+3

tuyệt vời, nó cũng đi kèm với đường ray 4.2.1 https://apidock.com/rails/v4.2.1/Hash/transform_values ​​ –

+1

Đây là giải pháp tốt nhất. Mã ngắn hơn và dễ đọc hơn. –

+0

@ZackXu Điều này là tốt hơn nhưng hãy để chúng tôi không quên thực tế là câu hỏi này đã được hỏi trong năm 2009 và hầu hết các câu trả lời được viết vào thời điểm đó. Nhanh chóng chuyển tiếp đến năm 2017, mọi thứ dễ dàng hơn nhiều đối với các biến đổi giá trị và khóa Hash vì các phiên bản Ruby mới hơn đã triển khai các phương thức đó. – rubyprince

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