Ruby:将数组转换为哈希时注入问题

时间:2012-05-13 20:41:48

标签: ruby

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}

然后工作。

红宝石如何处理第一次注射尝试没有得到我想要的东西? 有更好的方法吗?

4 个答案:

答案 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. 累加器的类型和块的返回类型必须相同
  2. 当未传递显式累加器时,累加器的类型与元素类型
  3. 相同

    在这种情况下,规则#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] }) }