Ruby inject with initial being a hash

后端 未结 3 565
囚心锁ツ
囚心锁ツ 2020-12-12 15:19

Can any one tell me why the following:

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

throws the error:

IndexError: string no         


        
相关标签:
3条回答
  • 2020-12-12 15:25

    Your block needs to return the accumulating hash:

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

    Instead, it's returning the string 'a' after the first pass, which becomes m in the next pass and you end up calling the string's []= method.

    0 讨论(0)
  • 2020-12-12 15:26

    Rather than using inject, you should look into Enumerable#each_with_object.

    Where inject requires you to return the object being accumulated into, each_with_object does it automatically.

    From the docs:

    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]
    

    So, closer to your question:

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

    Notice that inject and each_with_object reverse the order of the yielded parameters.

    0 讨论(0)
  • 2020-12-12 15:44

    The block must return the accumulator (the hash), as @Rob said. Some alternatives:

    With Hash#update:

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

    With Enumerable#each_with_object:

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

    With Hash#[]:

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

    With Array#to_h (Ruby >= 2.1):

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

    With Enumerable#mash from Facets:

    require 'facets'
    hash = ['a', 'b'].mash { |e| [e, e] }
    
    0 讨论(0)
提交回复
热议问题