2010-04-01 23 views
20

ho questa matrice, per esempio (la dimensione è variabile):Trova stringa più comune in una matrice

x = ["1.111", "1.122", "1.250", "1.111"] 

e ho bisogno di trovare il valore più commom ("1.111" in questo caso).

C'è un modo semplice per farlo?

Tks in anticipo!


EDIT # 1: Grazie a tutti per le risposte!


EDIT # 2: ho cambiato la mia risposta accettata in base alle informazioni di Z.E.D.. Grazie ancora a tutti!

risposta

43

Rubino < 2,2

#!/usr/bin/ruby1.8 

def most_common_value(a) 
    a.group_by do |e| 
    e 
    end.values.max_by(&:size).first 
end 

x = ["1.111", "1.122", "1.250", "1.111"] 
p most_common_value(x) # => "1.111" 

Nota: Enumberable.max_by è nuovo con Ruby 1.9, ma è stato backport per 1.8.7

Rubino> = 2.2

Rubino 2.2 introduce il metodo Object#itself , con cui possiamo rendere il codice più conciso:

def most_common_value(a) 
    a.group_by(&:itself).values.max_by(&:size).first 
end 

come una patch scimmia

O come Enumerable#mode:

Enumerable.class_eval do 
    def mode 
    group_by do |e| 
     e 
    end.values.max_by(&:size).first 
    end 
end 

["1.111", "1.122", "1.250", "1.111"].mode 
# => "1.111" 
+0

Sono impressionato dall'accelerazione rispetto al solito modo in cui lo farei. Bel lavoro. –

+0

@Wayne Conrad, soluzione superba. +1 –

+1

Ecco una versione più corta: x.group_by {| e | e} .values.max_by (&: size) .primo # => "1.111" Trasformarlo in un metodo se lo si desidera è lasciato come esercizio al lettore ;-) –

4

È possibile ordinare l'array e quindi ricollegarlo una volta. Nel ciclo, tieni traccia dell'elemento corrente e il numero di volte che viene visto. Una volta terminata la lista o modificata la voce, impostare max_count == count se count > max_count. E naturalmente tieni traccia di quale oggetto ha il max_count.

2

È possibile creare una mappa di hash che memorizza gli elementi della matrice come chiavi con i relativi valori che rappresentano il numero di volte in cui l'elemento viene visualizzato nella matrice.

pseudo codice:

["1.111", "1.122", "1.250", "1.111"].each { |num| 
    count=your_hash_map.get(num) 
    if(item==nil) 
    hashmap.put(num,1) 
    else 
    hashmap.put(num,count+1) 
} 

Come già accennato, l'ordinamento potrebbe essere più veloce.

+0

Perché la classificazione dovrebbe essere più veloce? L'ordinamento è O (n log n) al meglio mentre questa è O (n) – Pyrolistical

+0

Correzione, l'ordinamento basato sul confronto è O (n log n). Esistono tipi lineari, come ordinamento per bucket o ordinamento per radice. MODIFICA: in genere è necessario disporre di determinati tipi di dati per l'ordinamento del secchio o l'ordinamento digitale per essere davvero più efficienti rispetto agli ordinamenti di confronto. Ciò che compensano nel tempo di solito si consumano nello spazio. FTR, lo pseudo codice qui sopra è l'ordinamento del bucket. – saramah

2

Utilizzando la funzione valore di default di hash:

>> x = ["1.111", "1.122", "1.250", "1.111"] 
>> h = Hash.new(0) 
>> x.each{|i| h[i] += 1 } 
>> h.max{|a,b| a[1] <=> b[1] } 
["1.111", 2] 
+0

Questo è stato selezionato come risposta, ma guarda i risultati del benchmark che ho, visualizzati di seguito. –

+0

Non sarebbe 'new. (0)' risultato nello stesso oggetto per ogni oggetto hash? 'Hash.new {| h, k | h [k] = 0} 'invece? – karatedog

5

un solo passaggio attraverso l'hash di accumulare i conteggi. Utilizzare .max() per trovare la voce hash con il valore più grande.

 
#!/usr/bin/ruby 

a = Hash.new(0) 
["1.111", "1.122", "1.250", "1.111"].each { |num| 
    a[num] += 1 
} 

a.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2] 

o, rotolare il tutto in una sola riga:

 
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] } # => ["1.111", 2] 

Se solo si desidera che l'articolo indietro aggiungere.prima():

 
ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first # => "1.111" 

Il primo campione che ho usato è come sarebbe stato fatto in Perl di solito. Il secondo è più rubino. Entrambi funzionano con versioni precedenti di Ruby. Ho voluto confrontare loro, più vedere come la soluzione di Wayne potrebbe accelerare le cose in modo ho provato con riferimento:

 
#!/usr/bin/env ruby 

require 'benchmark' 

ary = ["1.111", "1.122", "1.250", "1.111"] * 1000 

def most_common_value(a) 
    a.group_by { |e| e }.values.max_by { |values| values.size }.first 
end 

n = 1000 
Benchmark.bm(20) do |x| 
    x.report("Hash.new(0)") do 
    n.times do 
     a = Hash.new(0) 
     ary.each { |num| a[num] += 1 } 
     a.max{ |a,b| a[1] <=> b[1] }.first 
    end 
    end 

    x.report("inject:") do 
    n.times do 
     ary.inject(Hash.new(0)){ |h,i| h[i] += 1; h }.max{ |a,b| a[1] <=> b[1] }.first 
    end 
    end 

    x.report("most_common_value():") do 
    n.times do 
     most_common_value(ary) 
    end 
    end 
end 

Ecco i risultati:

 
          user  system  total  real 
Hash.new(0)   2.150000 0.000000 2.150000 ( 2.164180) 
inject:    2.440000 0.010000 2.450000 ( 2.451466) 
most_common_value(): 1.080000 0.000000 1.080000 ( 1.089784) 
+0

molto, molto bello! grazie mille per questa informazione ... in realtà stavo leggendo su "benchmark" per farlo. grazie ancora. –

+0

Mostra perché il benchmarking è importante. Supponevo che l'uso di iniettare sarebbe stato più rapido di un loop sull'array usando ciascuno, ma la soluzione di Wayne ha dimezzato il tempo. –

+0

@ Z.E.D., Sto ricevendo un errore di sintassi, 'tIDENTIFIER inaspettato, in attesa di '}'' sulla riga 15, 'a.max {| a, b | a [1] b [1]} .prima', punto di riferimento a 'b ['. (Rubino 1.9.1). –

0

Esso ritornerà il valore più popolare in serie

x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0] 

IE:

x = ["1.111", "1.122", "1.250", "1.111"] 
# Most popular 
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[0] 
#=> "1.111 
# How many times 
x.group_by{|a| a }.sort_by{|a,b| b.size<=>a.size}.first[1].size 
#=> 2 
Problemi correlati