Подтвердить что ты не робот

В Ruby, как мне сделать хэш из массива?

У меня есть простой массив:

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

У меня также есть функция f, которая будет выполнять операцию с одним строковым вводом и возвращать значение. Эта операция очень дорогая, поэтому я хотел бы запомнить результаты в хеше.

Я знаю, что могу сделать желаемый хеш с чем-то вроде этого:

h = {}
arr.each { |a| h[a] = f(a) }

То, что я хотел бы сделать, это не инициализировать h, так что я могу просто написать что-то вроде этого:

h = arr.(???) { |a| a => f(a) }

Можно ли это сделать?

4b9b3361

Ответ 1

Скажите, что у вас есть функция с наивысшим названием: "f"

def f(fruit)
   fruit + "!"
end

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

предоставит вам:

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

Обновлено:

Как уже упоминалось в комментариях, Ruby 1.8.7 представляет более сильный синтаксис для этого:

h = Hash[arr.collect { |v| [v, f(v)] }]

Ответ 2

Были некоторые быстрые, грязные тесты на некоторые из этих ответов. (Эти результаты могут быть не совсем идентичными с вашей версией Ruby, странным кэшированием и т.д., Но общие результаты будут похожи.)

arr - это набор объектов ActiveRecord.

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

Бенчмарк @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

В заключение

Просто потому, что Ruby является выразительным и динамичным, не означает, что вы всегда должны искать самое подходящее решение. Основной цикл был самым быстрым в создании хэша.

Ответ 3

h = arr.each_with_object({}) { |v,h| h[v] = f(v) }

Ответ 4

Вот что я, вероятно, напишу:

h = Hash[arr.zip(arr.map(&method(:f)))]

Простой, ясный, очевидный, декларативный. Что еще вам нужно?

Ответ 7

Другой, немного более ясный ИМХО -

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

Использование длины как 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 >

Ответ 8

в дополнение к ответу Владо Цингеля (я пока не могу добавить комментарий, поэтому я добавил ответ).

Впрыск также можно использовать следующим образом: блок должен вернуть аккумулятор. Только назначение в блоке возвращает значение назначения, и сообщается об ошибке.

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