J'ai un tableau simple:
arr = ["apples", "bananas", "coconuts", "watermelons"]
J'ai également une fonction f
qui effectuera une opération sur une seule entrée de chaîne et renverra une valeur. Cette opération est très coûteuse, je voudrais donc mémoriser les résultats dans le hachage.
Je sais que je peux faire le hachage souhaité avec quelque chose comme ceci:
h = {}
arr.each { |a| h[a] = f(a) }
Ce que j'aimerais faire, c'est de ne pas avoir à initialiser h, pour pouvoir simplement écrire quelque chose comme ceci:
h = arr.(???) { |a| a => f(a) }
Cela peut-il être fait?
Disons que vous avez une fonction avec un nom funtastic: "f"
def f(fruit)
fruit + "!"
end
arr = ["apples", "bananas", "coconuts", "watermelons"]
h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]
te donnera:
{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}
Mis à jour:
Comme mentionné dans les commentaires, Ruby 1.8.7 introduit une syntaxe plus agréable pour cela:
h = Hash[arr.collect { |v| [v, f(v)] }]
A fait des repères rapides et sales sur certaines des réponses données. (Ces résultats peuvent ne pas être exactement identiques aux vôtres en fonction de Ruby, mise en cache étrange, etc. mais les résultats généraux seront similaires.)
arr
est une collection d'objets ActiveRecord.
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.1299999999999999998, @ utime = 1.51, @ total = 1.64
Tout simplement parce que Ruby est expressif et dynamique, cela ne signifie pas que vous devriez toujours opter pour la meilleure solution. La boucle de base de chaque boucle a été la plus rapide pour créer un hachage.
h = arr.each_with_object({}) { |v,h| h[v] = f(v) }
Voici ce que j'écrirais probablement:
h = Hash[arr.Zip(arr.map(&method(:f)))]
Simple, clair, évident, déclaratif. Que pourrais-tu vouloir de plus?
Ruby 2.6.0 permet une syntaxe plus courte en en passant un bloc au to_h
méthode :
arr.to_h { |a| [a, f(a)] }
Je le fais comme décrit dans ce grand 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)) }
Plus d'informations sur la méthode inject
: http://Ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject
Un autre, à mon humble avis un peu plus clair -
Hash[*array.reduce([]) { |memo, fruit| memo << fruit << f(fruit) }]
Utiliser la longueur comme 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 >