2012-02-24 58 views
45

任何一个可以告诉我为什么以下:红宝石注入初始是一个哈希

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

引发错误:

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>' 

,而下面的作品?

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

回答

63

你块需要返回累积散列:

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

相反,它返回字符串“A”的第一关,其应在下一行程变得m后你最终调用字符串的[]=方法。

+0

是否有必要在最后加入m?例如,如果块是'{| array,(k,v)|“数组<< << MyObject.new(k,v)}'会起作用吗?考虑到'array。<<'返回数组。 – Ziggy 2012-10-10 01:13:31

+8

@Ziggy:是的,这是必要的,因为赋值'hash [key] = value'返回'value',你需要'hash'。 – tokland 2012-11-10 18:53:28

43

该块必须返回累加器(散列),正如@Rob所说。一些备选方案:

随着Hash#update

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

随着Enumerable#each_with_object

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

随着Hash#[]

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

随着Enumerable#mash从方面:

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

随着Array#to_h(红宝石> = 2.1):

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

不错的选择。我特别喜欢原始海报的技巧和你的map-to-pairs-then-create-new-hash方法之间的区别:原始问题基本上是迭代的 - 对于每个项目,在Hash上执行此操作 - 因此注入似乎过于复杂(因此错误)。但是映射方法更多的是关于整体:将这些单数组合成一个数组对,然后将这个数组对做成一个哈希值。 – 2012-02-24 16:56:46

19

而不是使用注射,你应该看看Enumerable#each_with_object

其中inject需要您返回正在积累的物体,each_with_object会自动进行此操作。

从文档:

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] 

所以,更接近你的问题:

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

注意injecteach_with_object扭转产生参数的顺序。