Convertir un tableau en un index de hachage en Ruby
J'ai un tableau, et je veux faire un hash afin que je puisse rapidement se demander "est de X dans le tableau?".
En perl, il est facile (et rapide) pour ce faire:
my @array = qw( 1 2 3 );
my %hash;
@hash{@array} = undef;
Cela génère un hash qui ressemble à:
{
1 => undef,
2 => undef,
3 => undef,
}
Le meilleur que j'ai trouvé en Ruby, c'est:
array = [1, 2, 3]
hash = Hash[array.map {|x| [x, nil]}]
qui donne:
{1=>nil, 2=>nil, 3=>nil}
Est-il un meilleur Ruby façon?
EDIT 1
Non, Tableau.inclure? n'est pas une bonne idée. Son lent. Il fait une requête en O(n) au lieu de O(1). Mon exemple du tableau a trois éléments à des fins de concision; assumer le réel a des millions d'éléments. Faisons une petite analyse comparative:
#!/usr/bin/ruby -w
require 'benchmark'
array = (1..1_000_000).to_a
hash = Hash[array.map {|x| [x, nil]}]
Benchmark.bm(15) do |x|
x.report("Array.include?") { 1000.times { array.include?(500_000) } }
x.report("Hash.include?") { 1000.times { hash.include?(500_000) } }
end
Produit:
user system total real
Array.include? 46.190000 0.160000 46.350000 ( 46.593477)
Hash.include? 0.000000 0.000000 0.000000 ( 0.000523)
- N'oubliez pas de prendre en compte le temps qu'il faut pour faire la conversion. Bien sûr, si votre situation le permet, à l'aide d'un set pour commencer (comme suggéré par @Zach Langley) évite ce coût.
- Pour être juste, l'indice de référence ci-dessus devrait inclure la conversion à partir d'un tableau de hachage
- en théorie, c'est sûr. Dans la pratique, pas vraiment—c'est fondamentalement pas pertinent. La conversion est effectuée une fois, la recherche de beaucoup, beaucoup, beaucoup de fois. J'oublie ce que je travaillais lorsque j'ai demandé à ce, mais dans le programme de recherche a été probablement fait plusieurs millions de fois, après la conversion d'une fois.
Vous devez vous connecter pour publier un commentaire.
Si vous avez besoin de la valeur de hachage est l'adhésion, pensez à utiliser un
Set
:essayez celui-ci:
Hash[a.zip]
retourne également la même réponse.Vous pouvez faire cette astuce bien pratique:
Hash[ [1, 2, 3, 4].map {|k| [k, nil]} ]
? Il n'y a pas besoin d'un supplément pour la splat et les aplatir.Si vous voulez rapidement demander "est de X dans le tableau?", vous devez utiliser
Array#include?
.Modifier (en réponse à de plus en OP):
Si vous voulez speedy regarder les temps, l'utilisation d'un Ensemble. Avoir un Hash points à toutes les
nil
s est stupide. La Conversion est un processus facile, trop avecArray#to_set
.Résultats sur ma machine:
Vous devriez considérer l'utilisation d'un ensemble, pour commencer, au lieu d'un tableau, de sorte que la conversion n'est jamais nécessaire.
Je suis assez certain qu'il n'y a pas un one-shot moyen astucieux pour construire ce hash. Mon inclination serait d'être explicite et de l'état, ce que je fais:
Il n'a pas l'air particulièrement élégant, mais il est clair, et fait le travail.
FWIW, votre proposition initiale (sous Ruby 1.8.6 au moins) ne semble pas fonctionner. Je reçois un "ArgumentError: nombre impair d'arguments en faveur de Hachage" erreur. De hachage.[] s'attend à un littéral, même-lengthed liste de valeurs:
j'ai donc essayé de changer votre code:
mais la performance est à dire:
donne
À moins que je me manque quelque chose ici, une simple affectation de la boucle semble la plus claire et la plus efficace pour construire ce hachage.
Rampion me battre pour elle. Ensemble pourrait être la réponse.
Que vous pouvez faire:
Ta façon de créer de la valeur de hachage semble bon. J'ai eu un muck autour de la cisr et c'est une autre façon
array.each{|e| hash[e] = nil}
est 3,5 fois plus rapideJe pense que chrismearpoint sur l'utilisation de cession par rapport à la création est grande. Pour rendre la chose un peu plus Ruby-esque, bien que, je pourrais suggérons de commencer par quelque chose de autres que
nil
à chaque élément:Le problème de l'affectation à
nil
est que vous devez utiliserhas_key?
au lieu de[]
, depuis[]
vous donnernil
(votre valeur du marqueur) si leHash
n'a pas la clé spécifiée. Vous pourrait contourner ce problème en utilisant une valeur par défaut différente de la valeur, mais pourquoi passer par le travail supplémentaire?Peut-être que je suis mal comprendre le but ici; Si vous avez voulu savoir si X est dans le tableau, pourquoi ne pas le tableau.inclure?("X") ?
Faire quelques benchmarking sur les suggestions de la mesure donne que chrismear et Gaius de l'affectation en fonction de hachage création est légèrement plus rapide que ma méthode map (et l'attribution néant est légèrement plus rapide que d'attribuer la valeur true). mtyaka et rampion de l'Ensemble suggestion est d'environ 35% plus lent à créer.
Autant que les recherches,
hash.include?(x)
est une très petite quantité plus vite quehash[x]
; les deux sont deux fois plus rapide queset.include?(x)
.Analyse comparative de code est:
Si vous n'êtes pas gêné de ce que les valeurs de hachage sont
Prend une seconde sur mon bureau.
Si vous êtes à la recherche d'un équivalent de ce code Perl:
Vous pouvez simplement utiliser le simple code Ruby:
Voici une belle façon de cache recherches avec un Hachage:
À peu près ce qu'il fait est de créer un constructeur par défaut pour les nouvelles valeurs de hachage, puis les magasins "true" dans le cache si c'est dans le tableau (néant au contraire). Cela permet un chargement différé dans le cache, juste au cas où vous n'utilisez pas tous les éléments.
Cela préserve 0 si votre hash a été
[0,0,0,1,0]
Retourne :