a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']] a.inject({}) {|r, val| r[val[0]] = val[1]}
当我运行它时,我得到一个索引错误
当我将块更改为
时a.inject({}) {|r, val| r[val[0]] = val[1]; r}
然后工作。
红宝石如何处理第一次注射尝试没有得到我想要的东西? 有更好的方法吗?
答案 0 :(得分:13)
仅仅因为Ruby被动态地和隐式地输入并不意味着你不必考虑类型。
没有显式累加器的Enumerable#inject
类型(通常称为reduce
)类似于
reduce :: [a] → (a → a → a) → a
或者用我编写的更多Rubyish符号
Enumerable[A]#inject {|A, A| A } → A
您会注意到所有类型都是相同的。 Enumerable
的元素类型,块的两个参数类型,块的返回类型以及整个方法的返回类型。
带有显式累加器的Enumerable#inject
的类型(通常称为fold
)类似于
fold :: [b] → a → (a → b → a) → a
或
Enumerable[B]#inject(A) {|A, B| A } → A
在这里,您可以看到累加器可以具有不同的类型,而不是集合的元素类型。
这两条规则通常可以解决所有与Enumerable#inject
相关的类型问题:
在这种情况下,规则#1会咬你。当你做
之类的事情acc[key] = value
在您的块中,分配评估为指定的值,而不是作业的接收者。你必须用
替换它acc.tap { acc[key] = value }
另见Why Ruby inject method cannot sum up string lengths without initial value?
BTW:您可以使用destructuring bind来使您的代码更具可读性:
a.inject({}) {|r, (key, value)| r[key] = value; r }
答案 1 :(得分:9)
有一种更简单的方法 -
a = [[1, 'a'],[2, 'b'],[3, 'c'], [4, 'd']]
b = Hash[a] # {1=>"a", 2=>"b", 3=>"c", 4=>"d"}
第一种方法不起作用的原因是因为inject在下一次迭代中使用块的结果作为r
。对于第一次迭代,r
设置为您传递给它的参数,在本例中为{}
。
答案 2 :(得分:4)
第一个块将赋值结果返回到inject
,第二个块返回哈希值,因此它实际上有效。
很多人认为inject
使用反模式;考虑each_with_object
instead。
答案 3 :(得分:2)
a.inject({}) { |r, val| r.merge({ val[0] => val[1] }) }