web-dev-qa-db-fra.com

Unique sur un tableau de hachages basé sur la valeur

Je pense que cela pourrait être amélioré (un sentiment commun chez Ruby). J'essaie d'unifier un tableau de hachages basé sur la valeur. Dans cet exemple, je veux les couleurs des éléments. La mousse et la neige sont des imposteurs.

# remove unique array of hashes based on a hash value

a = [
  { :color => "blue", :name => "water" },
  { :color => "red", :name => "fire" },
  { :color => "white", :name => "wind" },
  { :color => "green", :name => "earth" },
  { :color => "green", :name => "moss" },
  { :color => "white", :name => "snow" }
]

# remove moss and snow
uniques = []
a.each_with_index do |r, i|
  colors = uniques.collect {|e| e[:color]}

  if !colors.include? r[:color]
    uniques.Push r
  else
    a[i] = nil
  end
end

a.compact!

puts a

Cela va imprimer

{:color=>"blue", :name=>"water"}
{:color=>"red", :name=>"fire"}
{:color=>"white", :name=>"wind"}
{:color=>"green", :name=>"earth"}

Ce qui est "correct" mais j'estime que c'est excessif. Mon expérience avec .map .inject est limitée et ces techniques avancées m'échappent. Si quelqu'un pouvait repenser cela, cela pourrait m'aider à comprendre une autre technique laconique.

31
squarism

Dans Ruby 1.9, essayez ce qui suit

a.uniq! {|e| e[:color] }
72
Steve Wilhelm

Je choisirais les méthodes reject ou select de Array:

require 'pp'

a = [
  { :color => "blue", :name => "water" },
  { :color => "red", :name => "fire" },
  { :color => "white", :name => "wind" },
  { :color => "green", :name => "earth" },
  { :color => "green", :name => "moss" },
  { :color => "white", :name => "snow" }
]

pp a.reject{ |h| %w[moss snow].include?( h[:name]) } 
# >> [{:color=>"blue", :name=>"water"},
# >>  {:color=>"red", :name=>"fire"},
# >>  {:color=>"white", :name=>"wind"},
# >>  {:color=>"green", :name=>"earth"}]

Alternativement, vous pouvez être positif à ce sujet et select ceux que vous souhaitez conserver:

pp a.select{ |h| %w[water fire wind earth].include?( h[:name] ) } 
# >> [{:color=>"blue", :name=>"water"},
# >>  {:color=>"red", :name=>"fire"},
# >>  {:color=>"white", :name=>"wind"},
# >>  {:color=>"green", :name=>"earth"}]

Vous ne vous occupez pas vraiment des hachages, c'est un tableau contenant des hachages, alors ne les laissez pas vous dérouter. Les méthodes matricielles telles que reject et select sont des méthodes essentielles pour filtrer les éléments indésirables ou les conserver.

Dans votre exemple de code, vous perdez de vue ce que votre objectif est: vous voulez les éléments, en rejetant "mousse" et "neige", qui ne sont pas des éléments. Filtrez les non-éléments, et les éléments corrects/réels se trouvent dans les hachages. De là, vous pouvez extraire les couleurs correctes.

Un problème supplémentaire à surveiller lors de l’utilisation de uniq est qu’il est positionnel, c’est-à-dire qu’il recherche la première valeur unique et rejette les valeurs suivantes. Cela n'était pas apparent dans votre code car votre tableau était toujours dans le même ordre que celui que vous avez testé. Si vous avez mélangé l'ordre cependant ...:

2.times do
  pp a.shuffle.uniq{ |h| h[:color] }
end

Pass # 1 ...

# [{:color=>"red", :name=>"fire"},
#  {:color=>"white", :name=>"wind"},
#  {:color=>"green", :name=>"moss"},
#  {:color=>"blue", :name=>"water"}]

Pass # 2 ...

# [{:color=>"green", :name=>"earth"},
#  {:color=>"blue", :name=>"water"},
#  {:color=>"red", :name=>"fire"},
#  {:color=>"white", :name=>"snow"}]

Soudain, nous voyons que "la mousse" et la "neige" se faufilent dans les résultats, même si les couleurs sont uniques. Ce sont des pièges subtils que vous devez surveiller. 

6
the Tin Man

Pour ceux qui voudraient une variante encore plus courte du réponse correcte de Steve Wilhelm ,

IL FAUT SE MÉFIER:

a.uniq!(&:color)

NE FONCTIONNERA PAS pour un tableau de hachages, exactement comme

a[1].color

ne fonctionnerait pas non plus.

Pour plus d'informations sur l'opérateur &, lisez ce lien , ou les commentaires sur cette question qui, à leur tour, contiennent de nombreux liens vers des ressources.

D’autre part, vous pourriez faire fonctionner la méthode Symbol # to_proc en utilisant lambdas, comme l’explique ici , bien que cela puisse compliquer les choses et ne constituerait certainement pas une version plus courte de la bonne réponse. Cependant, c'est une connaissance très intéressante.

Merci mukesh-kumar-gupta pour le heads-up

0
user2553863