296 votes

Ruby: moyen le plus simple de filtrer les clés de hachage?

J'ai un hash qui ressemble à ceci :

params = { :irrelevant => "Une chaîne",
           :choice1 => "Oh regardez, un autre",
           :choice2 => "Encore plus de chaînes",
           :choice3 => "Mais attendez",
           :irrelevant2 => "La dernière chaîne" }

Et je veux un moyen simple de rejeter toutes les clés qui ne sont pas choice+int. Ça pourrait être choice1, ou choice1 à choice10. Ça varie.

Comment puis-je isoler les clés avec juste le mot choice et un chiffre ou des chiffres après eux ?

En prime :

Transformez le hash en une chaîne avec une tabulation (\t) comme délimiteur. J'ai fait cela, mais cela a pris plusieurs lignes de code. Habituellement, les maîtres Rubiciens peuvent le faire en une seule ligne ou presque.

0 votes

En termes de la question bonus, pouvez-vous préciser à quoi vous aimeriez que ressemble la chaîne de caractères.

0 votes

Bien sûr, l'exemple de hachage ci-dessus donnerait : "Oh regarde, un autre\tEncore plus de chaînes\tMais attends" (sans \t à la fin de la chaîne, seulement entre elles)

0 votes

Votre chaîne d'exemple a été coupée dans le commentaire. Vous pouvez utiliser le lien de modification pour éditer votre question et ajouter l'exemple là-bas. Vous pouvez également publier le code Ruby que vous avez déjà créé comme exemple de ce que vous souhaitez réaliser.

398voto

mikej Points 30224

Modifier la réponse d'origine : Même si cette réponse (au moment de ce commentaire) est la réponse sélectionnée, la version d'origine de cette réponse est obsolète.

Je rajoute une mise à jour ici pour aider les autres à éviter de se laisser distraire par cette réponse comme je l'ai fait.

Comme l'autre réponse le mentionne, Ruby >= 2.5 a ajouté la méthode Hash#slice qui était auparavant uniquement disponible dans Rails.

Exemple :

> { un: 1, deux: 2, trois: 3 }.slice(:un, :deux)
=> {:un=>1, :deux=>2}

Fin de la modification. Ce qui suit est la réponse d'origine qui, je suppose, sera utile si vous utilisez Ruby < 2.5 sans Rails, bien que je pense que ce cas est assez rare à ce stade.


Si vous utilisez Ruby, vous pouvez utiliser la méthode select. Vous devrez convertir la clé d'un symbole en une chaîne pour faire correspondre le regex. Cela vous donnera un nouveau Hash avec seulement les choix dedans.

choix = params.select { |clé, valeur| clé.to_s.match(/^choix\d+/) }

ou vous pouvez utiliser delete_if et modifier le Hash existant par exemple.

params.delete_if { |clé, valeur| !clé.to_s.match(/choix\d+/) }

ou si ce sont juste les clés et pas les valeurs que vous voulez alors vous pouvez faire :

params.keys.select { |clé| clé.to_s.match(/^choix\d+/) }

et cela vous donnera simplement un tableau des clés par exemple [:choix1, :choix2, :choix3]

1 votes

Excellent! Une fois que je le vois, c'est tellement simple! Merci beaucoup, et merci aux autres qui ont pris le temps de répondre aussi.

2 votes

Les symboles peuvent être analysés avec des expressions régulières de nos jours, si je me souviens bien (IIRC).

0 votes

@Andrew, Je pense que tu as raison. J'étais sur 1.8.7 lorsque je testais la réponse à cette question.

312voto

lfx_cool Points 937

En Ruby, la méthode Hash#select est une bonne option. Si vous travaillez avec Rails, vous pouvez utiliser Hash#slice et Hash#slice!. par exemple (rails 3.2.13)

h1 = {:a => 1, :b => 2, :c => 3, :d => 4}

h1.slice(:a, :b)         # renvoie {:a=>1, :b=>2}, mais h1 n'est pas modifié

h2 = h1.slice!(:a, :b)   # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4}

15 votes

Faites attention aux clés de chaîne..h1 = {'a'=>1,:b=>2} ne renverra que {:b=>2} avec h1.slice(:a,:b)

0 votes

Super solution. J'ai utilisé ceci pour retourner le résultat dans rspec, où je voulais analyser une valeur d'un hash dans un hash. ``` test = {:a=>1, :b=>2, c=>{:A=>1, :B=>2}} solution==> test.c.slice(:B) ```

1 votes

Remplacez Rails par ActiveSupport et c'est parfait ;)

49voto

Nuno Costa Points 63

La manière la plus simple est d'inclure le gem 'activesupport' (ou gem 'active_support').

Ensuite, dans votre classe, vous avez seulement besoin de

require 'active_support/core_ext/hash/slice'

et d'appeler

params.slice(:choice1, :choice2, :choice3) # => {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}

Je pense que ça ne vaut pas la peine de déclarer d'autres fonctions qui pourraient avoir des bugs, et qu'il v'est mieux d'utiliser une méthode qui a été peaufinée au cours des dernières années.

0 votes

Activerecord n'est pas nécessaire pour cela, du moins en version 2.5.

6voto

ayckoster Points 2836

Si vous voulez le hash restant:

params.delete_if {|k, v| ! k.match(/choice[0-9]+/)}

ou si vous voulez juste les clés:

params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}

0 votes

Selon le nombre de choiceX et irrelevantX que vous avez, sélectionner OU supprimer_si pourrait être meilleur. Si vous avez plus de choiceX, supprimer_si est meilleur.

5voto

montrealmike Points 3795

Mettez ceci dans un initialiseur

class Hash
  def filter(*args)
    return nil if args.try(:empty?)
    if args.size == 1
      args[0] = args[0].to_s if args[0].is_a?(Symbol)
      self.select {|key| key.to_s.match(args.first) }
    else
      self.select {|key| args.include?(key)}
    end
  end
end

Ensuite, vous pouvez faire

{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # =>  {a: "1", b: "b"}

ou

{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/)  # =>  {a: "1"}

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