96 votes

En Ruby, comment créer un hachage à partir d'un tableau ?

J'ai un simple tableau :

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

J'ai également une fonction f qui effectue une opération sur une seule chaîne de caractères et renvoie une valeur. Cette opération étant très coûteuse, j'aimerais mémoriser les résultats dans le hachage.

Je sais que je peux faire le hachage souhaité avec quelque chose comme ça :

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

Ce que j'aimerais faire, c'est ne pas avoir à initialiser h, de sorte que je puisse simplement écrire quelque chose comme ceci :

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

Est-ce possible ?

143voto

microspino Points 4294

Supposons que vous ayez une fonction avec un nom amusant : "f"

def f(fruit)
   fruit + "!"
end

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

vous donnera :

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

Mise à 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)] }]

0 votes

Je pense que vous vouliez dire ... { |v| [v, f(v)] } mais celui-ci a fait l'affaire !

4 votes

Juste une chose : pourquoi y a-t-il un * à côté de *arr.collect ?

4 votes

@Jeriko - l'opérateur splat * rassemble une liste en un tableau ou déroule un tableau en une liste, selon le contexte. Ici, il déroule le tableau en une liste (qui servira d'éléments pour le nouveau hachage).

61voto

dmastylo Points 831

J'ai effectué quelques tests rapides sur certaines des réponses données. (Ces résultats peuvent ne pas être exactement identiques aux vôtres en fonction de la version de Ruby, de la mise en cache bizarre, 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, @compensation0.85000000000005

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, @courrier0.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, @circuit de vente de produits de consommation 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, @livraison1.64

En conclusion

Ce n'est pas parce que Ruby est expressif et dynamique qu'il faut toujours opter pour la plus belle solution. La boucle de base each a été la plus rapide pour créer un hash.

45voto

Timitry Points 471

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)] }

35voto

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

3 votes

C'est beaucoup plus concis que d'utiliser Hash[arr.collect{...}]

1 votes

C'est incroyablement lent, lisez mon article ci-dessous : stackoverflow.com/a/27962063/1761067

11voto

Jörg W Mittag Points 153275

Voici ce que j'écrirais probablement :

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

Simple, clair, évident, déclaratif. Que voulez-vous de plus ?

2 votes

J'aime zip que l'autre, mais comme nous appelons déjà les map Pourquoi ne pas en rester là ? h = Hash[ arr.map { |v| [ v, f(v) ] } ] Votre version présente-t-elle un avantage que je ne vois pas ?

0 votes

@Telemachus : Avec tout le code Haskell que j'ai lu, je me suis juste habitué à la programmation sans point, c'est tout.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X