54 votes

Comment grouper par nombre dans un tableau sans utiliser de boucle ?

arr = [1,2,1,3,5,2,4]

Comment puis-je compter le tableau par valeur de groupe avec tri ? J'ai besoin de la sortie suivante :

x[1] = 2  
x[2] = 2  
x[3] = 1  
x[4] = 1  
x[5] = 1

115voto

Michael Kohl Points 33345
x = arr.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }

36voto

sawa Points 62592

Disponible uniquement sous ruby 1.9

En gros, c'est la même chose que Réponse de Michael mais de façon un peu plus courte :

x = arr.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}

Dans des situations similaires,

  • Lorsque l'élément de départ est un mutable tel qu'un Array , Hash , String vous pouvez utiliser each_with_object comme dans le cas ci-dessus.
  • Lorsque l'élément de départ est un immuable tel que Numeric vous devez utiliser inject comme ci-dessous.

    sum = (1..10).inject(0) {|sum, n| sum + n} # => 55

27voto

Mr. Black Points 2342

Il existe une version courte qui se trouve dans ruby 2.7 => Enumerable#tally .

[1,2,1,3,5,2,4].tally  #=> { 1=>2, 2=>2, 3=>1, 5=>1, 4=>1 }

# Other possible usage

(1..6).tally { |i| i%3 }   #=> { 0=>2, 1=>2, 2=>2 }

20voto

vint-i-vuit Points 1157

Encore une autre approche - semblable à d'autres :

result=Hash[arr.group_by{|x|x}.map{|k,v| [k,v.size]}]
  1. Regroupez par la valeur de chaque élément.
  2. Mettez en correspondance le groupement avec un tableau de [valeur, compteur] paires.
  3. Transformez le tableau de paris en valeurs clés dans un Hash, c'est-à-dire accessibles via result[1]=2 ... .

13voto

fuzzygroup Points 116

Chaque fois que vous trouvez quelqu'un qui affirme que quelque chose est le plus rapide sur ce type de routine primitive, je trouve toujours intéressant de le confirmer, car sans confirmation, la plupart d'entre nous ne font que deviner. Donc j'ai pris toutes les méthodes ici et je les ai comparées.

J'ai pris un tableau de 120 liens que j'ai extraits d'une page Web et que je devais regrouper par nombre. J'ai mis en œuvre tous ces éléments en utilisant une boucle seconds = Benchmark.realtime et j'ai obtenu tous les temps.

Supposons que links soit le nom du tableau que je dois compter :

#0.00077
seconds = Benchmark.realtime do
  counted_links = {}
  links.each { |e| counted_links[e] = links.count(e) if counted_links[e].nil?}
end
seconds

#0.000232
seconds = Benchmark.realtime do
  counted_links = {}
  links.sort.group_by {|x|x}.each{|x,y| counted_links[x] = y.size}
end

#0.00076
seconds = Benchmark.realtime do 
  Hash[links.uniq.map{ |i| [i, links.count(i)] }]
end

#0.000107 
seconds = Benchmark.realtime do 
  links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}
end

#0.000109
seconds = Benchmark.realtime do 
  links.each_with_object(Hash.new(0)) {|e, h| h[e] += 1}
end

#0.000143
seconds = Benchmark.realtime do 
  links.inject(Hash.new(0)) { |h, e| h[e] += 1 ; h }
end

Et ensuite un peu de ruby pour trouver la réponse :

times = [0.00077, 0.000232, 0.00076, 0.000107, 0.000109, 0.000143].min
==> 0.000107

Donc la méthode la plus rapide, selon vous bien sûr, est :

links.inject(Hash.new(0)) {|h, v| h[v] += 1; h}

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