119 votes

En Ruby, existe-t-il une méthode Array combinant 'select' et 'map'?

J'ai un tableau Ruby contenant des valeurs de chaîne. J'ai besoin de:

  1. Trouver tous les éléments qui correspondent à un prédicat
  2. Exécuter les éléments correspondants via une transformation
  3. Renvoyer les résultats sous forme de tableau

En ce moment ma solution ressemble à ceci:

 def example
  matchingLines = @lines.select{ |line| ... }
  results = matchingLines.map{ |line| ... }
  return results.uniq.sort
end
 

Existe-t-il une méthode Array ou Enumerable combinant sélection et mappage en une seule instruction logique?

124voto

Jed Schneider Points 6253

J'utilise habituellement map et compact avec mes critères de sélection comme postfix si. Compact se débarrasse des nils.

 jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}    
 => [3, 3, 3, nil, nil, nil] 


jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact
 => [3, 3, 3] 
 

57voto

Adam Lindberg Points 9689

Vous pouvez utiliser de réduire les pour cette, qui ne nécessite qu'un seul passage:

[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a }
=> [3, 3, 3] 

En d'autres termes, initialiser l'état d'être ce que vous voulez (dans notre cas, une liste vide à remplir: []), alors assurez-vous de toujours retourner cette valeur avec des modifications pour chaque élément dans la liste d'origine (dans notre cas, l'élément modifié poussé à la liste).

C'est le plus efficace car il ne passe en boucle sur la liste avec un seul passage (map + select ou compact nécessite deux phases.

Dans votre cas:

def example
  results = @lines.reduce([]) do |lines, line|
    lines.push( ...(line) ) if ...
    lines
  end
  return results.uniq.sort
end

9voto

Gishu Points 59012

Pas sûr qu'il y en ait un (Le module Enumerable qui ajoute select et map - n'en montre pas un). - parce que cela nécessiterait que vous passiez en deux blocs à la méthode select_and_transform, ce qui serait un peu inintéressant.

Évidemment, vous pouvez simplement les chaîner, ce qui est plus lisible.

transformed_list = lines.select{|line| ...}.map{|line| ... }

2voto

Daniel Points 7197

Non, mais vous pouvez le faire comme ceci:

 lines.map { |line| do_some_action if check_some_property  }.reject(&:nil?)
 

Ou même mieux:

 lines.inject([]) { |all, line| all << line if check_some_property; all }
 

1voto

Jörg W Mittag Points 153275
def example
  @lines.select {|line| ... }.map {|line| ... }.uniq.sort
end

En Ruby 1.9 et 1.8.7, vous pouvez également la chaîne et enveloppez-les itérateurs par tout simplement pas le passage d'un bloc à eux:

enum.select.map {|bla| ... }

Mais ce n'est pas vraiment possible dans ce cas, puisque les types de bloc de valeurs de retour d' select et map ne correspondent pas. Il fait plus de sens pour quelque chose comme ceci:

enum.inject.with_index {|(acc, el), idx| ... }

AFAICS, le meilleur que vous pouvez faire, c'est le premier exemple.

Voici un petit exemple:

%w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["a", "b", "c", "d"]

%w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["A", "B", false, false, "C", "D"]

Mais ce que vous vraiment voulez est - ["A", "B", "C", "D"].

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