Tôi muốn thay đổi mọi giá trị trong hàm băm để thêm '%' trước và sau giá trị
{ :a=>'a' , :b=>'b' }
phải đổi thành
{ :a=>'%a%' , :b=>'%b%' }
Cách tốt nhất để làm điều này là gì?
Tôi muốn thay đổi mọi giá trị trong hàm băm để thêm '%' trước và sau giá trị
{ :a=>'a' , :b=>'b' }
phải đổi thành
{ :a=>'%a%' , :b=>'%b%' }
Cách tốt nhất để làm điều này là gì?
Câu trả lời:
Nếu bạn muốn chính các chuỗi thực sự biến đổi tại chỗ (có thể và ảnh hưởng đáng kể đến các tham chiếu khác đến cùng các đối tượng chuỗi):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
Nếu bạn muốn hàm băm thay đổi tại chỗ, nhưng bạn không muốn ảnh hưởng đến các chuỗi (bạn muốn nó có được chuỗi mới):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |key,str| my_hash[key] = "%#{str}%" }
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
Nếu bạn muốn băm mới:
# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]
# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]
Hash.[]
không chấp nhận một mảng các cặp mảng, nó yêu cầu số lượng đối số trực tiếp chẵn (do đó sẽ xuất hiện ở phía trước).
Hash#each
mang lại cả khóa và giá trị cho khối. Trong trường hợp này, tôi không quan tâm đến chìa khóa, và vì vậy tôi không đặt tên cho nó bất cứ điều gì hữu ích. Tên biến có thể bắt đầu bằng dấu gạch dưới và trên thực tế có thể chỉ là dấu gạch dưới. Không có lợi ích hiệu suất khi làm điều này, nó chỉ là một ghi chú tự ghi tinh tế rằng tôi không làm gì với giá trị khối đầu tiên đó.
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, phải trả lại hàm băm từ khối
Trong Ruby 2.1 trở lên, bạn có thể làm
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
như được chỉ ra bởi sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Ruby 2.4 đã giới thiệu phương pháp Hash#transform_values!
mà bạn có thể sử dụng.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(không có tiếng nổ), không sửa đổi máy thu. Nếu không, một câu trả lời tuyệt vời, cảm ơn!
Cách tốt nhất để sửa đổi các giá trị của Hash tại chỗ là
hash.update(hash){ |_,v| "%#{v}%" }
Ít mã và ý định rõ ràng. Cũng nhanh hơn vì không có đối tượng mới được phân bổ ngoài các giá trị phải được thay đổi.
gsub!
.
update
truyền đạt ý định tốt hơn merge!
. Tôi nghĩ rằng đây là câu trả lời tốt nhất.
k
, sử dụng _
thay thế.
Một chút dễ đọc hơn, map
nó là một mảng băm đơn yếu tố và reduce
vớimerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(for map
) sau đó a Hash
. Sau đó, mỗi bước của thao tác giảm sẽ nhân đôi "ghi nhớ" Hash
và thêm cặp khóa-giá trị mới vào đó. Ít nhất sử dụng :merge!
trong reduce
để thay đổi trận chung kết Hash
tại chỗ. Và cuối cùng, bạn không sửa đổi các giá trị của đối tượng hiện tại mà tạo ra một đối tượng mới, đó không phải là những gì câu hỏi được hỏi.
nil
nếu the_hash
trống
Có một phương thức 'Rails way' mới cho tác vụ này :) http://api.rubyonrails.org/groupes/Hash.html#method-i-transform_values
Hash#transform_values
. Đây nên là cách để đi từ bây giờ.
Một phương pháp không giới thiệu tác dụng phụ cho bản gốc:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Bản đồ Hash # cũng có thể là một cách đọc thú vị vì nó giải thích lý do tại sao Hash.map
không trả lại Hash (đó là lý do tại sao Mảng các [key,value]
cặp kết quả được chuyển đổi thành Hash mới) và cung cấp các cách tiếp cận thay thế cho cùng một mẫu chung.
Chúc mừng mã hóa.
[Tuyên bố miễn trừ trách nhiệm: Tôi không chắc liệu Hash.map
ngữ nghĩa có thay đổi trong Ruby 2.x]
Hash.map
ngữ nghĩa thay đổi trong Ruby 2.x không?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
trong Ruby 1.9 (IIRC) tránh việc truy cập trực tiếp vào tên và Map#merge
cũng có thể hoạt động. Không chắc chắn làm thế nào các chi tiết phức tạp khác nhau.
Sau khi thử nghiệm nó với RSpec như thế này:
describe Hash do
describe :map_values do
it 'should map the values' do
expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
end
end
end
Bạn có thể triển khai Hash # map_values như sau:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
Các chức năng sau đó có thể được sử dụng như thế này:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Nếu bạn tò mò biến thể inplace nào là nhanh nhất ở đây:
Calculating -------------------------------------
inplace transform_values! 1.265k (± 0.7%) i/s - 6.426k in 5.080305s
inplace update 1.300k (± 2.7%) i/s - 6.579k in 5.065925s
inplace map reduce 281.367 (± 1.1%) i/s - 1.431k in 5.086477s
inplace merge! 1.305k (± 0.4%) i/s - 6.630k in 5.080751s
inplace each 1.073k (± 0.7%) i/s - 5.457k in 5.084044s
inplace inject 697.178 (± 0.9%) i/s - 3.519k in 5.047857s