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

J’ai un hash qui ressemble à ceci:

params = { :irrelevant => "A Ssortingng", :choice1 => "Oh look, another one", :choice2 => "Even more ssortingngs", :choice3 => "But wait", :irrelevant2 => "The last ssortingng" } 

Et je veux un moyen simple de rejeter toutes les clés qui ne sont pas choisies + int. Cela pourrait être choix1, ou choix1 par choix10. Cela varie.

Comment puis-je isoler les touches avec juste le choix du mot et un chiffre ou des chiffres après eux?

Prime:

Tournez le hachage dans une chaîne avec l’onglet (\ t) en tant que délimiteur. Je l’ai fait, mais il a fallu plusieurs lignes de code. Habituellement, les maîtres Rubicians peuvent le faire en une ou plusieurs lignes.

REMARQUE: Si vous utilisez Rails, vous pouvez utiliser Hash.slice comme indiqué par lfx_cool dans la réponse ci-dessous.

Si vous utilisez Ruby, vous pouvez utiliser la méthode select . Vous devrez convertir la clé d’un symbole en chaîne pour effectuer la correspondance avec l’expression rationnelle. Cela vous donnera un nouveau Hash avec juste les choix.

 choices = params.select { |key, value| key.to_s.match(/^choice\d+/) } 

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

 params.delete_if { |key, value| !key.to_s.match(/choice\d+/) } 

ou si ce ne sont que les clés et non les valeurs que vous voulez, alors vous pouvez faire:

 params.keys.select { |key| key.to_s.match(/^choice\d+/) } 

et cela donnera le juste un tableau des clés, par exemple [:choice1, :choice2, :choice3]

Dans Ruby, la sélection Hash # 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) # return {:a=>1, :b=>2}, but h1 is not changed h2 = h1.slice!(:a, :b) # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4} 

Le moyen le plus simple consiste à inclure la gem 'activesupport' (ou gem 'active_support' ).

Ensuite, dans votre classe, il vous suffit de

 require 'active_support/core_ext/hash/slice' 

et d’appeler

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

Je crois que cela ne vaut pas la peine de déclarer d’autres fonctions susceptibles d’avoir des bogues, et il est préférable d’utiliser une méthode qui a été modifiée au cours des dernières années.

Le moyen le plus simple consiste à inclure la gem ‘ActiveSupport’ (ou gem ‘active_support’).

params.slice(:choice1, :choice2, :choice3)

Ceci est une ligne pour résoudre la question originale complète:

 params.select { |k,_| k[/choice/]}.values.join('\t') 

Mais la plupart des solutions ci-dessus résolvent un cas où vous devez connaître les clés à l’avance, en utilisant slice ou simple regexp.

Voici une autre approche qui fonctionne pour des cas d’utilisation simples et plus complexes, qui est permutable à l’exécution

 data = {} matcher = ->(key,value) { COMPLEX LOGIC HERE } data.select(&matcher) 

Désormais, non seulement cela permet une logique plus complexe pour faire correspondre les clés ou les valeurs, mais elle est également plus facile à tester et vous pouvez échanger la logique correspondante à l’exécution.

Ex pour résoudre le problème d’origine:

 def some_method(hash, matcher) hash.select(&matcher).values.join('\t') end params = { :irrelevant => "A Ssortingng", :choice1 => "Oh look, another one", :choice2 => "Even more ssortingngs", :choice3 => "But wait", :irrelevant2 => "The last ssortingng" } some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more ssortingngs\\tBut wait" some_method(params, ->(_,v) { v[/ssortingng/]}) # => "Even more ssortingngs\\tThe last ssortingng" 

Si vous travaillez avec des rails et que vous avez les clés dans une liste séparée, vous pouvez utiliser la notation * :

 keys = [:foo, :bar] hash1 = {foo: 1, bar:2, baz: 3} hash2 = hash1.slice(*keys) => {foo: 1, bar:2} 

Comme d’autres réponses l’ont indiqué, vous pouvez également utiliser la slice! pour modifier le hachage en place (et renvoyer la clé / les valeurs effacées).

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 

Alors tu peux 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"} 

Avec Hash::select :

 params = params.select { |key, value| /^choice\d+$/.match(key.to_s) } 

Si vous voulez le hachage 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]+/)} 
 params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t") 

En ce qui concerne la question bonus:

  1. Si vous avez #select méthode #select comme ceci (liste des tableaux à 2 éléments):

     [[:choice1, "Oh look, another one"], [:choice2, "Even more ssortingngs"], [:choice3, "But wait"]] 

    puis prenez simplement ce résultat et exécutez:

     filtered_params.join("\t") # or if you want only values instead of pairs key-value filtered_params.map(&:last).join("\t") 
  2. Si vous avez #delete_if méthode #delete_if comme ceci (hash):

     {:choice1=>"Oh look, another one", :choice2=>"Even more ssortingngs", :choice3=>"But wait"} 

    puis:

     filtered_params.to_a.join("\t") # or filtered_params.values.join("\t") 
 params = { :irrelevant => "A Ssortingng", :choice1 => "Oh look, another one", :choice2 => "Even more ssortingngs", :choice3 => "But wait", :irrelevant2 => "The last ssortingng" } choices = params.select { |key, value| key.to_s[/^choice\d+/] } #=> {:choice1=>"Oh look, another one", :choice2=>"Even more ssortingngs", :choice3=>"But wait"}