web-dev-qa-db-fra.com

Ruby: Comment trouver un élément dans un tableau qui a le plus d'occurrences?

[1, 1, 1, 2, 3].mode
=> 1

['cat', 'dog', 'snake', 'dog'].mode
=> dog
45
Til

Commencez par créer un hash mappant chaque valeur du tableau sur sa fréquence…

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

freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
#=> {1=>3, 2=>1, 3=>1}

… Puis utilisez le tableau des fréquences pour trouver l'élément avec la fréquence la plus élevée:

arr.max_by { |v| freq[v] }
#=> 1
80
Sophie Alpert
array.max_by { |i| array.count(i) }
15
Nathan

Mike: J'ai trouvé une méthode plus rapide. Essaye ça:

  class Array
    def mode4
      group_by{|i| i}.max{|x,y| x[1].length <=> y[1].length}[0]
    end
  end

La sortie de référence: 

                                    user     system      total        real
mode1                          24.340000   0.070000  24.410000 ( 24.526991)
mode2                           0.200000   0.000000   0.200000 (  0.195348)
mode3                           0.120000   0.000000   0.120000 (  0.118200)
mode4                           0.050000   0.010000   0.060000 (  0.056315)
     mode1 = 76
     mode2 = 76
     mode3 = [[76, 18]]
     mode4 = 76
13
Nilesh C
arr = [ 1, 3, 44, 3 ]
most_frequent_item = arr.uniq.max_by{ |i| arr.count( i ) }
puts most_frequent_item
#=> 3

Inutile de penser aux mappages de fréquences.

9
etringer

Ceci est une copie de cette question: Ruby - Eléments uniques dans Array

Voici la solution de cette question: 

group_by { |n| n }.values.max_by(&:size).first

Cette version semble être encore plus rapide que la réponse de Nilesh C. Voici le code que j'ai utilisé pour le comparer (OS X 10.6 Core 2 2,4 GHz Mo).

Félicitations à Mike Woodhouse pour le code de référence (original):

class Array
   def mode1
     group_by { |n| n }.values.max_by(&:size).first
   end
   def mode2
     freq = inject(Hash.new(0)) { |h,v| h[v] += 1; h }
     max = freq.values.max                   # we're only interested in the key(s) with the highest frequency
     freq.select { |k, f| f == max }         # extract the keys that have the max frequency
   end
end

arr = Array.new(1_0000) { |i| Rand(100000) }    # something to test with

Benchmark.bm(30) do |r|
    (1..2).each do |i| r.report("mode#{i}") { 100.times do arr.send("mode#{i}").inspect; end }; end
end

Et voici les résultats du benchmark:

                                user     system      total        real
mode1                           1.830000   0.010000   1.840000 (  1.876642)
mode2                           2.280000   0.010000   2.290000 (  2.382117)
 mode1 = 70099
 mode2 = [[70099, 3], [70102, 3], [51694, 3], [49685, 3], [38410, 3], [90815, 3], [30551, 3], [34720, 3], [58373, 3]]

Comme vous pouvez le constater, cette version est environ 20% plus rapide avec l’avertissement de ne pas tenir compte des liens. J'aime aussi la brièveté, je l'utilise personnellement tel quel, sans appliquer de patch pour le singe partout. :)

7
Brandon

si vous essayez d'éviter d'apprendre #inject (ce que vous ne devriez pas faire ...)

words = ['cat', 'dog', 'snake', 'dog']
count = Hash.new(0)

words.each {|Word| count[Word] += 1}
count.sort_by { |k,v| v }.last

mais si je lisais cette réponse avant, maintenant je ne saurais rien sur #inject and man, vous devez savoir sur #inject.

2
jj_
idx = {}
[2,2,1,3,1].each { |i| idx.include?(i) ? idx[i] += 1 : idx[i] = 1}

Ceci est juste un indexeur simple. Vous pouvez remplacer le tableau [2,2,1 ..] par tout type d'identifiant basé sur un symbole/une chaîne, cela ne fonctionnerait pas avec les objets, vous auriez besoin d'introduire un peu plus de complexité, mais c'est assez simple.

en relisant vos questions, cette solution est un peu trop élaborée car elle va vous renvoyer un index de toutes les occurrences, pas seulement celle qui en contient le plus.

1
Derek P.

Voici une autre version qui vous donne les liens comme un mode devrait:

def mode
  group_by {|x| x}.group_by {|k,v| v.size}.sort.last.last.map(&:first)
end

En d'autres termes, regroupez les valeurs, puis regroupez ces paires kv par le nombre de valeurs, puis triez ces paires kv, prenez le dernier groupe de tailles (le plus élevé), puis déroulez ses valeurs. J'aime group_by.

1
glenn mcdonald
def mode(array)

    count = []  # Number of times element is repeated in array
    output = [] 
    array.compact!
    unique = array.uniq
    j=0

    unique.each do |i|
        count[j] = array.count(i)
        j+=1
    end
    k=0
    count.each do |i|
        output[k] = unique[k] if i == count.max
        k+=1
    end  

    return output.compact.inspect
end

p mode([3,3,4,5]) #=> [3]

p mode([1,2,3]) #=> [1,2,3]

p mode([0,0,0,0,0,1,2,3,3,3,3,3]) #=> [0,3]

p mode([-1,-1,nil,nil,nil,0]) #=> [-1]

p mode([-2,-2,3,4,5,6,7,8,9,10,1000]) #=> [-2]
0
Hemchandra