2012-02-24 23 views
45

Bất kỳ một thể cho tôi biết tại sao như sau:của Ruby tiêm với ban đầu là một hash

['a', 'b'].inject({}) {|m,e| m[e] = e } 

ném lỗi:

IndexError: string not matched 
     from (irb):11:in `[]=' 
     from (irb):11:in `block in irb_binding' 
     from (irb):11:in `each' 
     from (irb):11:in `inject' 
     from (irb):11 
     from C:/Ruby192/bin/irb:12:in `<main>' 

trong khi các công trình sau đây?

a = {} 
a["str"] = "str" 

Trả lời

63

khối của bạn cần phải trả lại băm tích lũy:

['a', 'b'].inject({}) {|m,e| m[e] = e; m } 

Thay vào đó, nó trở về chuỗi 'a' đèo đầu tiên, trở thành m ở đèo tiếp theo và sau khi bạn kết thúc gọi phương thức []= của chuỗi.

+0

Hoàn toàn cần thiết để bao gồm m ở cuối? Ví dụ, nếu khối là '{| mảng, (k, v) | mảng << MyObject.new (k, v)} 'có hoạt động không? Xem xét 'mảng. <<' trả về mảng. – Ziggy

+8

@ Ziggy: có, nó là cần thiết vì việc gán 'băm [key] = value' trả về' giá trị', và bạn cần 'băm'. – tokland

43

Khối phải trả lại bộ tích lũy (băm), như @Rob nói. Một số lựa chọn thay thế:

Với Hash#update:

hash = ['a', 'b'].inject({}) { |m, e| m.update(e => e) } 

Với Enumerable#each_with_object:

hash = ['a', 'b'].each_with_object({}) { |e, m| m[e] = e } 

Với Hash#[]:

hash = Hash[['a', 'b'].map { |e| [e, e] }] 

Với Enumerable#mash từ khía cạnh:

require 'facets' 
hash = ['a', 'b'].mash { |e| [e, e] } 

Với Array#to_h (Ruby> = 2.1):

hash = ['a', 'b'].map { |e| [e, e] }.to_h 
+3

Lựa chọn thay thế tốt. Tôi đặc biệt thích sự khác biệt giữa kỹ thuật của poster ban đầu và phương pháp tiếp cận bản đồ-to-pair-then-create-new-hash của bạn: câu hỏi ban đầu về cơ bản là lặp lại - cho mỗi mục, thực hiện thao tác này trên Hash quá phức tạp (do đó lỗi). Nhưng cách tiếp cận ánh xạ là nhiều hơn về wholes: làm cho mảng đơn này thành một mảng các cặp, sau đó biến mảng đó thành một băm. –

19

Thay vì sử dụng tiêm, bạn nên xem xét Enumerable#each_with_object.

Trường hợp inject yêu cầu bạn trả lại đối tượng đang được tích lũy vào, each_with_object tự động thực hiện.

Từ các tài liệu:

Iterates the given block for each element with an arbitrary object given, and returns the initially given object.

If no block is given, returns an enumerator.

e.g.:

evens = (1..10).each_with_object([]) {|i, a| a << i*2 } 
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 

Vì vậy, gần gũi hơn với câu hỏi của bạn:

[1] pry(main)> %w[a b].each_with_object({}) { |e,m| m[e] = e } 
=> {"a"=>"a", "b"=>"b"} 

ý rằng injecteach_with_object đảo ngược thứ tự của các thông số mang lại.

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