In Ruby, how do I make a hash from an array?

后端 未结 8 1050
谎友^
谎友^ 2020-12-12 19:28

I have a simple array:

arr = [\"apples\", \"bananas\", \"coconuts\", \"watermelons\"]

I also have a function f that will perfo

相关标签:
8条回答
  • 2020-12-12 19:52

    Did some quick, dirty benchmarks on some of the given answers. (These findings may not be exactly identical with yours based on Ruby version, weird caching, etc. but the general results will be similar.)

    arr is a collection of ActiveRecord objects.

    Benchmark.measure {
        100000.times {
            Hash[arr.map{ |a| [a.id, a] }]
        }
    }
    

    Benchmark @real=0.860651, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.8500000000000005, @total=0.8500000000000005

    Benchmark.measure { 
        100000.times {
            h = Hash[arr.collect { |v| [v.id, v] }]
        }
    }
    

    Benchmark @real=0.74612, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=0.740000000000002, @total=0.750000000000002

    Benchmark.measure {
        100000.times {
            hash = {}
            arr.each { |a| hash[a.id] = a }
        }
    }
    

    Benchmark @real=0.627355, @cstime=0.0, @cutime=0.0, @stime=0.010000000000000009, @utime=0.6199999999999974, @total=0.6299999999999975

    Benchmark.measure {
        100000.times {
            arr.each_with_object({}) { |v, h| h[v.id] = v }
        }
    }
    

    Benchmark @real=1.650568, @cstime=0.0, @cutime=0.0, @stime=0.12999999999999998, @utime=1.51, @total=1.64

    In conclusion

    Just because Ruby is expressive and dynamic, doesn't mean you should always go for the prettiest solution. The basic each loop was the fastest in creating a hash.

    0 讨论(0)
  • 2020-12-12 19:54

    Say you have a function with a funtastic name: "f"

    def f(fruit)
       fruit + "!"
    end
    
    arr = ["apples", "bananas", "coconuts", "watermelons"]
    h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]
    

    will give you:

    {"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}
    

    Updated:

    As mentioned in the comments, Ruby 1.8.7 introduces a nicer syntax for this:

    h = Hash[arr.collect { |v| [v, f(v)] }]
    
    0 讨论(0)
  • 2020-12-12 19:55

    in addition to the answer of Vlado Cingel (I cannot add a comment yet, so I added an answer).

    Inject can also be used in this way: the block has to return the accumulator. Only the assignment in the block returns the value of the assignment, and an error is reported.

    array = ["apples", "bananas", "coconuts", "watermelons"]
    hash = array.inject({}) { |h,fruit| h[fruit]= f(fruit); h }
    
    0 讨论(0)
  • 2020-12-12 19:59

    Ruby 2.6.0 enables a shorter syntax by passing a block to the to_h method:

    arr.to_h { |a| [a, f(a)] }
    
    0 讨论(0)
  • 2020-12-12 19:59

    Another one, slightly clearer IMHO -

    Hash[*array.reduce([]) { |memo, fruit| memo << fruit << f(fruit) }]
    

    Using length as f() -

    2.1.5 :026 > array = ["apples", "bananas", "coconuts", "watermelons"]
     => ["apples", "bananas", "coconuts", "watermelons"] 
    2.1.5 :027 > Hash[*array.reduce([]) { |memo, fruit| memo << fruit << fruit.length }]
     => {"apples"=>6, "bananas"=>7, "coconuts"=>8, "watermelons"=>11} 
    2.1.5 :028 >
    
    0 讨论(0)
  • 2020-12-12 20:06

    I'm doing it like described in this great article http://robots.thoughtbot.com/iteration-as-an-anti-pattern#build-a-hash-from-an-array

    array = ["apples", "bananas", "coconuts", "watermelons"]
    hash = array.inject({}) { |h,fruit| h.merge(fruit => f(fruit)) }
    

    More info about inject method: http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject

    0 讨论(0)
提交回复
热议问题