2011-11-29 29 views
13

Có một tay ngắn hoặc thực hành tốt nhất để gán thứ gì đó vào một hàm băm khi chúng không có trong ruby ​​không? Ví dụ, vấn đề của tôi là tôi đang sử dụng một băm khác để xây dựng này và nếu một cái gì đó trong nó là nil, nó gán nil cho khóa đó, thay vì chỉ để nó một mình. Tôi hiểu lý do tại sao điều này xảy ra vì vậy giải pháp của tôi là:Không chỉ định giá trị nil cho hàm băm

hash1[:key] = hash2[:key] unless hash2[:key].nil? 

Vì tôi không thể có giá trị trong trường hợp khóa thực sự trỏ đến không. (Tôi thà có một băm rỗng hơn một cái có {: key => nil}, điều đó không thể xảy ra)

Câu hỏi của tôi là có cách nào tốt hơn để làm điều này? Tôi không muốn làm một delete_if ở phần cuối của bài tập.

+1

giải pháp của bạn có vẻ tốt đẹp đối với tôi. Nếu bạn đưa ra toàn bộ vòng lặp như một ví dụ, sẽ có một số cách tốt để làm cho nó thành một dòng, tôi chắc chắn. –

Trả lời

16

một chút hash1 của ngắn hơn nếu bạn phủ nhận "trừ khi" tuyên bố

hash1[:key] = hash2[:key] if hash2[:key] # same as if ! hash2[:key].nil? 

bạn cũng có thể làm việc so sánh trong câu hỏi & & như được đề xuất trong các câu trả lời khác của Michael hoặc Marc-Andre

Điều đó thực sự tùy thuộc vào bạn, điều bạn cảm thấy dễ đọc nhất đối với bạn. Theo thiết kế, luôn có nhiều cách để Ruby giải quyết vấn đề.

Bạn cũng có thể thay đổi hash2:

hash1 = hash2.reject{|k,v| v.nil?} 

hash2.reject!{|k,v| v.nil?} # even shorter, if in-place editing of hash2 

này sẽ loại bỏ cặp khóa/giá trị: (! Tại chỗ, nếu bạn sử dụng từ chối) key => nil từ hash2

+0

> false.nil? => false – nroose

2

Làm thế nào về một cái gì đó như thế này?

hash2.each_pair do |key, value| 
    next if value.nil? 
    hash1[key] = value 
end 

Nếu bạn đang làm chỉ là một nhiệm vụ duy nhất, điều này có thể cạo một vài ký tự:

hash2[:key] && hash1[:key] = hash2[:key] 

ví dụ đầu tiên của tôi cũng có thể được cạo một chút nữa:

hash2.each_pair{ |k,v| v && hash1[k] = v } 

Tôi nghĩ đầu tiên là dễ đọc/dễ hiểu nhất. Ngoài ra, các ví dụ 2 và 3 sẽ bỏ qua bất kỳ thứ gì đánh giá sai (nil hoặc false). Ví dụ cuối cùng này là một dòng và sẽ không bỏ qua false giá trị:

hash2.each_pair{ |k,v| v.nil? || hash1[k] = v } 
3

Tôi không chắc chắn nếu đó là thực sự bất kỳ tốt hơn, nhưng

hash2[:key] && hash[:key] = hash2[:key] 

có thể làm việc. Lưu ý rằng điều này sẽ hành xử theo cách tương tự cho falsenil, nếu đó không phải những gì bạn muốn

!hash2[:key].nil? && hash[:key] = hash2[:key] 

sẽ tốt hơn. Tất cả điều này giả định rằng :key sẽ là một giá trị tùy ý mà bạn có thể không có quyền kiểm soát.

+0

+1 đúng. Nhưng về cơ bản cũng giống như giải pháp của Red – Tilo

+0

Tất nhiên nó cũng giống nhau. Cách tôi hiểu câu hỏi, anh ta không hài lòng với phong cách của mã, không phải là chức năng. –

3

Tôi tin rằng cách tốt nhất là sao chép giá trị nil lên giá trị băm. Nếu một người vượt qua một tùy chọn :foo => nil, điều đó có thể có nghĩa là một cái gì đó và phải ghi đè lên mặc định :foo của 42. Điều này cũng làm cho nó dễ dàng hơn để có các tùy chọn mà nên mặc định true, mặc dù người ta nên sử dụng fetch trong những trường hợp:

opt = hash.fetch(:do_cool_treatment, true) # => will be true if key is not present 

Có rất nhiều cách để sao chép trên các giá trị, bao gồm nil hoặc false.

Đối với một chìa khóa duy nhất, bạn có thể sử dụng has_key? thay vì tra cứu:

hash1[:key] = hash2[:key] if hash2.has_key? :key 

Đối với tất cả (hoặc nhiều) phím, sử dụng merge!:

hash1.merge!(hash2) 

Nếu bạn chỉ muốn làm điều này đối với một vài phím của hash2, bạn có thể cắt khóa:

hash1.merge!(hash2.slice(:key, ...)) 
+0

Nếu 'hash2' có các khóa có giá trị' nil', 'merge!' Sẽ không hoạt động vì 'hash1' sẽ có cùng các khóa và giá trị' nil' của chúng ... chính xác những gì OP muốn tránh. –

+0

hợp nhất có bất lợi là nó sẽ sao chép một cặp: key => nil khóa/giá trị thành hash1 nếu nó tồn tại trong hash2. Tôi nghĩ OP không muốn điều đó. – Tilo

+0

@Tilo: Đã chỉnh sửa để phản ánh ý kiến ​​của tôi rằng tốt nhất nên sao chép các giá trị 'nil'. –

1

OK, do đó, nếu hợp nhất không hoạt động vì bạn muốn kiểm soát nhiều hơn:

hash1[:key] = hash2.fetch(:key, hash1[:key]) 

Điều này sẽ đặt hash1's: key là hash2, trừ khi nó không tồn tại. Trong trường hợp đó, nó sẽ sử dụng các giá trị mặc định (lập luận thứ 2 để lấy), đó là chìa khóa

+0

điều này sẽ không hoạt động nếu: khóa không phải là đã có trong hash1 (bên tay phải) –

+0

@ RickR Bạn đang phải ... doh! ... cũng cần phải tìm nạp ở phía bên phải: 'hash1 [: key] = hash2.fetch (: key, hash1.fetch (: key, nil))' ...và điều đó có vẻ hơi xấu xí –

3

Tôi thích này tốt nhất, vòng lặp và có điều kiện ghi đè tất cả trong một dòng!

h1 = {:foo => 'foo', :bar => 'bar'} 
h2 = {:foo => 'oof', :bar => nil} 

h1.merge!(h2) { |key, old_val, new_val| new_val.nil? ? old_val : new_val } 

#=> {:foo => 'oof', :bar => 'bar'} 

Điều này sẽ thay thế mọi giá trị trong h1 bằng giá trị h2 trong đó các khóa giống nhau và giá trị h2 không phải là 0.

+0

Tác vụ này có hoạt động khi 'h1' bắt đầu trống không? – millimoose

+0

có và không:/Nếu bạn có giá trị nil trong h2 và h1 trống, nó sẽ đặt giá trị trong h1 thành không. – Tilo

0

Thêm phần này vào initializers bạn hash.rb

class Hash 
    def set_safe(key,val) 
    if val && key 
     self[key] = val 
    end 
    end 
end 

sử dụng

hash = {} 
hash.set_safe 'key', value_or_nil 
Các vấn đề liên quan