Anzahl, Größe, Länge ... zu viele Auswahlmöglichkeiten in Ruby?

140

Ich kann anscheinend keine endgültige Antwort darauf finden und möchte sicherstellen, dass ich dies bis zur "n-ten Ebene" verstehe :-)

    a = {"a" => "Hallo", "b" => "Welt"}
    a.count # 2
    a.size # 2
    a.Länge # 2

    a = [10, 20]
    a.count # 2
    a.size # 2
    a.Länge # 2

Also welche verwenden? Wenn ich wissen möchte, ob a mehr als ein Element hat, scheint es keine Rolle zu spielen, aber ich möchte sicherstellen, dass ich den wirklichen Unterschied verstehe. Dies gilt auch für Arrays. Ich bekomme die gleichen Ergebnisse.

Außerdem ist mir klar, dass Anzahl / Größe / Länge mit ActiveRecord unterschiedliche Bedeutungen haben. Ich interessiere mich momentan hauptsächlich für reinen Ruby (1.92), aber wenn jemand den Unterschied, den AR macht, mitmachen möchte, wäre das auch sehr willkommen.

Vielen Dank!

cbmeeks
quelle
5
Das Phänomen, auf das Sie gestoßen sind, wird manchmal als TMTOWTDI bezeichnet : Es gibt mehr als einen Weg, dies zu tun. Dieser Slogan stammt aus der Perl-Community und Perl ist einer der Einflussfaktoren auf Ruby.
Andrew Grimm
Dies sind normalerweise Aliase für einander - sie tun dasselbe. Es gibt eine Methode, die Sie ebenfalls berücksichtigen sollten: Array#nitemsDiese gibt die Anzahl der Nicht-NIL-Elemente in einem Array zurück. Aber das ist nicht mehr in Ruby 1.9 verfügbar
Tilo

Antworten:

194

Für Arrays und Hashes size ist ein Alias ​​für length. Sie sind Synonyme und machen genau das Gleiche.

count ist vielseitiger - es kann ein Element oder Prädikat aufnehmen und nur die übereinstimmenden Elemente zählen.

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

Wenn Sie keinen Parameter zum Zählen angeben, hat dies im Grunde den gleichen Effekt wie die Aufruflänge. Es kann jedoch einen Leistungsunterschied geben.

Wir können dem Quellcode für Array entnehmen, dass sie fast genau dasselbe tun. Hier ist der C-Code für die Implementierung von array.length:

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

Und hier ist der relevante Teil aus der Implementierung von 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..
    }
}

Der Code für array.countführt einige zusätzliche Überprüfungen durch, ruft jedoch am Ende genau denselben Code auf : LONG2NUM(RARRAY_LEN(ary)).

Hashes ( Quellcode ) hingegen scheinen keine eigene optimierte Version von zu implementieren, countdaher wird die Implementierung von Enumerable( Quellcode ) verwendet, die alle Elemente durchläuft und sie einzeln zählt.

Im Allgemeinen würde ich empfehlen, length(oder seinen Alias) zu verwendensize ) zu verwenden, anstatt countzu wissen, wie viele Elemente es insgesamt gibt.


In Bezug auf ActiveRecord gibt es dagegen wichtige Unterschiede. Schauen Sie sich diesen Beitrag an:

Mark Byers
quelle
10

Es gibt einen entscheidenden Unterschied für Anwendungen, die Datenbankverbindungen verwenden.

Wenn Sie viele ORMs (ActiveRecord, DataMapper usw.) verwenden, wird allgemein verstanden, dass .size eine Abfrage generiert, die alle Elemente aus der Datenbank anfordert ('select * from mytable') und Ihnen dann die Anzahl der Elemente angibt Dies führt dazu, dass .count eine einzelne Abfrage generiert ('select count (*) from mytable'), die erheblich schneller ist.

Weil diese ORMs so weit verbreitet sind, folge ich dem Prinzip des geringsten Erstaunens. Wenn ich bereits etwas im Speicher habe, verwende ich im Allgemeinen .size. Wenn mein Code eine Anforderung an eine Datenbank (oder einen externen Dienst über eine API) generiert, verwende ich .count.

stef
quelle
1
Hierbei ist etwas zu beachten counter_cache. Wenn Sie eine Tabelle haben foound has_many bar, haben Sie eine Spalte mit dem fooNamen bars_count, die jedes Mal aktualisiert wird, wenn eine barerstellt / zerstört wird. Mit foo.bars.sizewird diese Spalte überprüft (ohne tatsächlich eine abzufragen bars). foo.bars.countführt die eigentliche Abfrage aus, die den Zweck des Caches zunichte machen würde.
Dudo
7

In den meisten Fällen (z. B. Array oder String ) sizeist ein Alias für length.

countkommt normalerweise von Enumerable und kann einen optionalen Prädikatblock annehmen. So enumerable.count {cond}ist [ungefähr](enumerable.select {cond}).length - es kann natürlich die Zwischenstruktur umgehen, da es nur die Anzahl der übereinstimmenden Prädikate benötigt.

Hinweis: Ich bin nicht sicher , ob count Kräfte eine Auswertung der Zählung , wenn der Block nicht angegeben ist oder wenn es um Kurzschlüsse zu dem , lengthwenn möglich.

Bearbeiten (und dank Marks Antwort!): count Ohne Block (zumindest für Arrays) wird keine Auswertung erzwungen. Ich nehme an, ohne formales Verhalten ist es "offen" für andere Implementierungen, wenn das Erzwingen einer Bewertung ohne Prädikat überhaupt Sinn macht.


quelle
5

Ich habe eine gute Antwort unter http://blog.hasmanythrough.com/2008/2/27/count-length-size gefunden

In ActiveRecord gibt es verschiedene Möglichkeiten, um herauszufinden, wie viele Datensätze sich in einer Zuordnung befinden, und es gibt einige subtile Unterschiede in der Funktionsweise.

post.comments.count - Bestimmen Sie die Anzahl der Elemente mit einer SQL COUNT-Abfrage. Sie können auch Bedingungen angeben, um nur eine Teilmenge der zugeordneten Elemente zu zählen (z. B.: Bedingungen => {: Autorenname => "josh"}). Wenn Sie einen Zähler-Cache für die Zuordnung einrichten, gibt #count diesen zwischengespeicherten Wert zurück, anstatt eine neue Abfrage auszuführen.

post.comments.length - Hiermit wird immer der Inhalt der Zuordnung in den Speicher geladen und anschließend die Anzahl der geladenen Elemente zurückgegeben. Beachten Sie, dass dadurch keine Aktualisierung erzwungen wird, wenn die Zuordnung zuvor geladen wurde und dann neue Kommentare auf andere Weise erstellt wurden (z. B. Comment.create (...) anstelle von post.comments.create (...)).

post.comments.size - Dies funktioniert als Kombination der beiden vorherigen Optionen. Wenn die Sammlung bereits geladen wurde, gibt sie ihre Länge zurück, genau wie beim Aufrufen von #length. Wenn es noch nicht geladen wurde, ist es so, als würde man #count aufrufen.

Auch ich habe eine persönliche Erfahrung:

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

Wir haben eine mehr Möglichkeiten , um herauszufinden , wie viele Elemente in einem Array wie .length, .countund .size. Es ist jedoch besser zu verwenden array.sizeals array.count. Weil die .sizeLeistung besser ist.

Venkat M.
quelle
1

Hinzufügen von mehr zu Mark Byers Antwort. In Ruby ist die Methode array.sizeein Alias ​​für die Array # -Längenmethode. Es gibt keinen technischen Unterschied bei der Verwendung einer dieser beiden Methoden. Möglicherweise sehen Sie auch keinen Leistungsunterschied. Das array.countmacht aber auch den gleichen Job, aber mit einigen zusätzlichen Funktionen Array # count

Es kann verwendet werden, um die Gesamtzahl der Elemente basierend auf einer bestimmten Bedingung zu ermitteln. Count kann auf drei Arten aufgerufen werden:

Array # count # Gibt die Anzahl der Elemente im Array zurück

Array # count n # Gibt die Anzahl der Elemente mit dem Wert n im Array zurück

Array # count {| i | i.even?} Gibt die Anzahl basierend auf der Bedingung zurück, die für jedes Elementarray aufgerufen wurde

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

Hier machen alle drei Methoden den gleichen Job. Hier wird das allerdings countinteressant.

Angenommen, ich möchte herausfinden, wie viele Array-Elemente das Array mit dem Wert 2 enthält

array.count 2    # => 3

Das Array besteht aus insgesamt drei Elementen mit dem Wert 2.

Jetzt möchte ich alle Array-Elemente größer als 4 finden

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

Das Array hat insgesamt 6 Elemente, die> als 4 sind.

Ich hoffe, es gibt einige Informationen über die countMethode.

Prabhakar Undurthi
quelle