Conteggio, dimensioni, lunghezza ... troppe scelte in Ruby?


140

Non riesco a trovare una risposta definitiva su questo e voglio assicurarmi di averlo compreso fino all'ennesimo livello :-)

    a = {"a" => "Ciao", "b" => "Mondo"}
    a.count # 2
    a. dimensione n. 2
    a.length # 2

    a = [10, 20]
    a.count # 2
    a. dimensione n. 2
    a.length # 2

Quindi quale usare? Se voglio sapere se a ha più di un elemento, allora non sembra importare, ma voglio essere sicuro di capire la vera differenza. Questo vale anche per gli array. Ottengo gli stessi risultati.

Inoltre, mi rendo conto che count / size / length hanno significati diversi con ActiveRecord. Sono per lo più interessato al puro Ruby (1.92) in questo momento, ma se qualcuno volesse cimentarsi con la differenza che fa l'AR sarebbe apprezzato anche.

Grazie!


5
Il fenomeno che hai incontrato è talvolta chiamato TMTOWTDI : c'è più di un modo per farlo. Questo slogan proviene dalla comunità Perl e Perl è una delle influenze su Ruby.
Andrew Grimm,

questi di solito sono alias l'uno per l'altro - fanno lo stesso. C'è anche un metodo da tenere presente: Array#nitemsche restituisce il numero di elementi non NIL in un array. Ma questo non è più disponibile in Ruby 1.9
Tilo,

Risposte:


194

Per matrici e hash sizeè un alias per length. Sono sinonimi e fanno esattamente la stessa cosa.

count è più versatile: può prendere un elemento o predicato e contare solo quegli elementi che corrispondono.

> [1,2,3].count{|x| x > 2 }
=> 1

Nel caso in cui non si fornisca un parametro per il conteggio, ha sostanzialmente lo stesso effetto della lunghezza della chiamata. Tuttavia, può esserci una differenza di prestazioni.

Dal codice sorgente di Array possiamo vedere che fanno quasi esattamente la stessa cosa. Ecco il codice C per l'implementazione di array.length:

static VALUE
rb_ary_length(VALUE ary)
{
    long len = RARRAY_LEN(ary);
    return LONG2NUM(len);
}

Ed ecco la parte rilevante dall'attuazione di array.count:

static VALUE
rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
    long n = 0;

    if (argc == 0) {
        VALUE *p, *pend;

        if (!rb_block_given_p())
            return LONG2NUM(RARRAY_LEN(ary));

        // etc..
    }
}

Il codice per array.countfa qualche controllo in più, ma alla fine chiama lo stesso codice esatto: LONG2NUM(RARRAY_LEN(ary)).

Gli hash ( codice sorgente ) d'altra parte non sembrano implementare la propria versione ottimizzata, countquindi viene utilizzata l'implementazione da Enumerable( codice sorgente ), che scorre su tutti gli elementi e li conta uno a uno.

In generale, consiglierei di usare length(o il suo alias size) piuttosto che countse volete sapere quanti elementi ci sono del tutto.


Per quanto riguarda ActiveRecord, d'altra parte, ci sono differenze importanti. controlla questo post:


10

Esiste una differenza cruciale per le applicazioni che utilizzano connessioni al database.

Quando si utilizzano molti ORM (ActiveRecord, DataMapper, ecc.) La comprensione generale è che .size genererà una query che richiede tutti gli elementi dal database ('select * from mytable') e quindi fornisce il numero di elementi risultante, mentre .count genererà una singola query ('select count (*) from mytable') che è considerevolmente più veloce.

Poiché questi ORM sono così diffusi seguo il principio del minimo stupore. In generale, se ho già qualcosa in memoria, allora uso .size e se il mio codice genererà una richiesta a un database (o servizio esterno tramite un'API), utilizzo .count.


1
Qualcosa da considerare con questo è counter_cache. Se hai una tabella, fooe has_many bar, avrai una colonna nel foonome bars_countche viene aggiornata ogni volta che barviene creata / distrutta. L'utilizzo foo.bars.sizeè ciò che controlla quella colonna (senza effettivamente interrogare nessuno bars). foo.bars.countesegue la query effettiva, che vanificherebbe lo scopo della cache.
Dudo,

7

Nella maggior parte dei casi (ad esempio Array o String ) sizeè un alias per length.

countnormalmente proviene da Enumerable e può accettare un blocco predicato opzionale. Quindi enumerable.count {cond}è [approssimativamente] (enumerable.select {cond}).length- può ovviamente bypassare la struttura intermedia in quanto ha solo bisogno del conteggio dei predicati corrispondenti.

Nota: non sono sicuro se count forza una valutazione dell'enumerazione se il blocco non è specificato o se è in cortocircuito verso lengthse possibile.

Modifica (e grazie alla risposta di Mark!): count Senza un blocco (almeno per gli array) non impone una valutazione. Suppongo che senza un comportamento formale sia "aperto" per altre implementazioni, se forzare una valutazione senza un predicato abbia mai davvero senso comunque.


5

Ho trovato una buona risposta su http://blog.hasmanythrough.com/2008/2/27/count-length-size

In ActiveRecord, ci sono diversi modi per scoprire quanti record ci sono in un'associazione e ci sono alcune sottili differenze nel modo in cui funzionano.

post.comments.count: determina il numero di elementi con una query SQL COUNT. È inoltre possibile specificare le condizioni per contare solo un sottoinsieme degli elementi associati (ad esempio: condizioni => {: nome_autore => "josh"}). Se si imposta una cache contatore sull'associazione, #count restituirà quel valore memorizzato nella cache invece di eseguire una nuova query.

post.comments.length: carica sempre il contenuto dell'associazione in memoria, quindi restituisce il numero di elementi caricati. Si noti che ciò non imporrà un aggiornamento se l'associazione è stata precedentemente caricata e quindi i nuovi commenti sono stati creati in un altro modo (ad esempio Comment.create (...) anziché post.comments.create (...)).

post.comments.size - Funziona come una combinazione delle due opzioni precedenti. Se la raccolta è già stata caricata, restituirà la sua lunghezza proprio come chiamando #length. Se non è stato ancora caricato, è come chiamare #count.

Inoltre ho un'esperienza personale:

<%= h(params.size.to_s) %> # works_like_that !
<%= h(params.count.to_s) %> # does_not_work_like_that !

2

Abbiamo un diversi modi per scoprire quanti elementi di un array come .length, .counte .size. Tuttavia, è meglio usare array.sizepiuttosto che array.count. Perché .sizeè migliore nelle prestazioni.


1

Aggiungendo altro alla risposta di Mark Byers. In Ruby il metodo array.sizeè un alias del metodo lunghezza # dell'array . Non vi è alcuna differenza tecnica nell'uso di uno di questi due metodi. Forse non vedrai alcuna differenza anche nelle prestazioni. Tuttavia, array.countfa anche lo stesso lavoro ma con alcune funzionalità extra Array # count

Può essere utilizzato per ottenere il numero totale di elementi in base a una condizione. Il conteggio può essere chiamato in tre modi:

Array # count # Restituisce il numero di elementi nell'array

Array # count n # Restituisce il numero di elementi con valore n in Array

Array # count {| i | i.even?} Restituisce il conteggio in base alla condizione invocata su ciascun array di elementi

array = [1,2,3,4,5,6,7,4,3,2,4,5,6,7,1,2,4]

array.size     # => 17
array.length   # => 17
array.count    # => 17

Qui tutti e tre i metodi fanno lo stesso lavoro. Tuttavia qui è dove countdiventa interessante.

Diciamo, voglio trovare quanti elementi dell'array contiene l'array con valore 2

array.count 2    # => 3

La matrice ha un totale di tre elementi con valore pari a 2.

Ora, voglio trovare tutti gli elementi dell'array maggiori di 4

array.count{|i| i > 4}   # =>6

L'array ha un totale di 6 elementi che sono> di 4.

Spero che fornisca alcune informazioni sul countmetodo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.