Jeff Ancel
Jeff Ancel

Reputation: 3091

Ruby Array.inject issue -- Can't see it

I am trying to store values from an array, to a hash (array value is the key, value just 0). Here is my code. Any ideas?

[1, 2, 3, 4].inject({}) {|result, e| result[e] = 0} 

This is the error I am getting.

oMethodError: undefined method `[]=' for 0:Fixnum
    from (irb):1
    from (irb):1:in `inject'
    from (irb):1:in `each'
    from (irb):1:in `inject'
    from (irb):1
    from :0

Upvotes: 4

Views: 957

Answers (6)

Bert
Bert

Reputation: 101

If your array is a simple array that doesn't have other arrays nested, then I would use the array-dereferencing hash construction method:

Hash[*[1,2,3,4].zip(Array.new(4,0)).flatten]

Or probably more generalized:

If there are nested arrays, you need to only flatten one level deep, and since there isn't a ruby command for flatten_once, you'll just have to do it manually through concatenation. The upside is you can interleave the zeros during concatenation so you no longer have to zip it:

Hash[*[1,2,3,4].inject([]){|s,x| s.concat([x,0])}]

Quickly benching 1000000 iterations of each on my machine in Ruby 1.8.6 gives me:

aforementioned merge! method:       34s
Hash[*...).concat([x,0])}] method:  25s
aforementioned result! method:      22s
Hash[*...).flatten] method:         15s

Upvotes: 0

Andrew Grimm
Andrew Grimm

Reputation: 81490

It'd take two lines, but you could also do

hash = {}
[1,2,3,4].each{|key| hash[key] = 0}

Upvotes: 0

glenn mcdonald
glenn mcdonald

Reputation: 15478

The "; result" thing works fine, but as a matter of taste, I prefer this way:

[1,2,3,4].inject({}) {|result,e| result.merge!(e=>0)}

If this is in performance-critical code, though, taste has its price. Here's a quick benchmark doing this operation a million times.

In Ruby 1.8.5

merge: 22s
merge!: 14s
; result: 9s

In Ruby 1.9.1

merge: 18s
merge!: 11s
; result: 5s

Upvotes: 8

guitarzan
guitarzan

Reputation:

You really ought to use merge! instead of merge in this case. There's no reason to create a new hash on every iteration.

[1,2,3,4].inject({}) {|result,e| result.merge!(e=>0)}

Upvotes: 0

ezpz
ezpz

Reputation: 12027

The issue is that result[e] = 0 returns the result of the operation, namely 0, and that is carried to the next iteration where you try to call []= on it. You can get past this by doing the following:

[1, 2, 3, 4].inject({}) {|result, e| result[e] = 0; result }

Upvotes: 8

sepp2k
sepp2k

Reputation: 370112

The return value of result[e] = 0 is 0, not result. You have to do:

[1, 2, 3, 4].inject({}) {|result, e| result[e] = 0; result}

(or use merge instead of []= or use each instead of inject)

Upvotes: 2

Related Questions