Nombre, taille, longueur… trop de choix en Ruby?

140

Je n'arrive pas à trouver de réponse définitive à ce sujet et je veux m'assurer de bien comprendre cela au "nième niveau" :-)

    a = {"a" => "Bonjour", "b" => "Monde"}
    compte n ° 2
    a. taille # 2
    a.longueur # 2

    a = [10, 20]
    compte n ° 2
    a. taille # 2
    a.longueur # 2

Alors, lequel utiliser? Si je veux savoir si a a plus d'un élément, cela ne semble pas avoir d'importance, mais je veux m'assurer de bien comprendre la vraie différence. Cela s'applique également aux tableaux. J'ai les mêmes résultats.

De plus, je me rends compte que nombre / taille / longueur ont des significations différentes avec ActiveRecord. Je suis surtout intéressé par le pur Ruby (1.92) en ce moment, mais si quelqu'un veut faire la différence avec l'AR, cela serait également apprécié.

Merci!

cbmeeks
la source
5
Le phénomène que vous avez rencontré est parfois appelé TMTOWTDI : il y a plus d'une façon de le faire. Ce slogan vient de la communauté Perl, et Perl est l'une des influences sur Ruby.
Andrew Grimm
ce sont généralement des alias les uns pour les autres - ils font de même. Il y a une méthode que vous devez également garder à l'esprit:, Array#nitemsqui renvoie le nombre d'éléments non NIL dans un tableau. Mais ce n'est plus disponible dans Ruby 1.9
Tilo

Réponses:

194

Pour les tableaux et les hachages sizeest un alias pour length. Ce sont des synonymes et font exactement la même chose.

count est plus polyvalent - il peut prendre un élément ou un prédicat et ne compter que les éléments qui correspondent.

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

Dans le cas où vous ne fournissez pas de paramètre pour compter, cela a fondamentalement le même effet que la longueur d'appel. Il peut cependant y avoir une différence de performance.

Nous pouvons voir dans le code source de Array qu'ils font presque exactement la même chose. Voici le code C pour l'implémentation de array.length:

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

Et voici la partie pertinente de la mise en œuvre de 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..
    }
}

Le code array.countfait quelques vérifications supplémentaires mais à la fin appelle exactement le même code: LONG2NUM(RARRAY_LEN(ary)).

Les hachages ( code source ) par contre ne semblent pas implémenter leur propre version optimisée de, countdonc l'implémentation de Enumerable( code source ) est utilisée, qui itère sur tous les éléments et les compte un par un.

En général, je conseillerais d'utiliser length(ou son alias size) plutôt que countsi vous voulez savoir combien d'éléments il y a au total.


En ce qui concerne ActiveRecord, en revanche, il existe des différences importantes. consultez ce post:

Mark Byers
la source
10

Il existe une différence cruciale pour les applications qui utilisent des connexions de base de données.

Lorsque vous utilisez de nombreux ORM (ActiveRecord, DataMapper, etc.), la compréhension générale est que .size va générer une requête qui demande tous les éléments de la base de données ('select * from mytable') et vous donne ensuite le nombre d'éléments résultant, alors que .count générera une seule requête ('select count (*) from mytable') qui est considérablement plus rapide.

Parce que ces ORM sont si répandus, je suis le principe du moindre étonnement. En général, si j'ai déjà quelque chose en mémoire, j'utilise .size, et si mon code va générer une requête vers une base de données (ou un service externe via une API), j'utilise .count.

stef
la source
1
Quelque chose à considérer avec ceci est counter_cache. Si vous avez une table, fooet qu'elle en a beaucoup bar, vous aurez une colonne dans foonamed bars_countqui sera mise à jour à chaque fois qu'une barest créée / détruite. L'utilisation foo.bars.sizeest ce qui vérifie cette colonne (sans en interroger réellement bars). foo.bars.countfait la requête réelle, ce qui irait à l'encontre de l'objectif du cache.
Dudo
7

Dans la plupart des cas (par exemple, Array ou String ) sizeest un alias pour length.

countprovient normalement de Enumerable et peut prendre un bloc de prédicat facultatif. Ainsi enumerable.count {cond}est [en gros] (enumerable.select {cond}).length- il peut bien sûr contourner la structure intermédiaire car il a juste besoin du nombre de prédicats correspondants.

Remarque: Je ne sais pas si count force une évaluation de l'énumération si le bloc n'est pas spécifié ou s'il court-circuite le lengthsi possible.

Edit (et grâce à la réponse de Mark!): count Sans bloc (du moins pour les tableaux) ne force pas une évaluation. Je suppose que sans comportement formel, il est "ouvert" pour d'autres implémentations, si le fait de forcer une évaluation sans prédicat a même un sens de toute façon.


la source
5

J'ai trouvé un bon logiciel sur http://blog.hasmanythrough.com/2008/2/27/count-length-size

Dans ActiveRecord, il existe plusieurs façons de savoir combien d'enregistrements se trouvent dans une association, et il existe des différences subtiles dans leur fonctionnement.

post.comments.count - Détermine le nombre d'éléments avec une requête SQL COUNT. Vous pouvez également spécifier des conditions pour ne compter qu'un sous-ensemble des éléments associés (par exemple: conditions => {: author_name => "josh"}). Si vous configurez un cache de compteur sur l'association, #count renverra cette valeur mise en cache au lieu d'exécuter une nouvelle requête.

post.comments.length - Cela charge toujours le contenu de l'association en mémoire, puis renvoie le nombre d'éléments chargés. Notez que cela ne forcera pas une mise à jour si l'association avait été précédemment chargée et que de nouveaux commentaires ont été créés d'une autre manière (par exemple Comment.create (...) au lieu de post.comments.create (...)).

post.comments.size - Cela fonctionne comme une combinaison des deux options précédentes. Si la collection a déjà été chargée, elle retournera sa longueur comme pour appeler #length. S'il n'a pas encore été chargé, c'est comme appeler #count.

J'ai aussi une expérience personnelle:

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

Nous avons plusieurs façons de savoir combien d'éléments dans un tableau comme .length, .countet .size. Cependant, il vaut mieux utiliser array.sizeplutôt que array.count. Parce que .sizec'est mieux en performance.

Venkat M
la source
1

Ajout de plus à la réponse de Mark Byers. Dans Ruby, la méthode array.sizeest un alias de la méthode Array # length . Il n'y a aucune différence technique dans l'utilisation de l'une de ces deux méthodes. Vous ne verrez peut-être pas non plus de différence de performances. Cependant, le fait array.countégalement le même travail mais avec quelques fonctionnalités supplémentaires Array # count

Il peut être utilisé pour obtenir le nombre total d'éléments en fonction de certaines conditions. Count peut être appelé de trois manières:

Nombre de tableaux # Renvoie le nombre d'éléments dans Array

Array # count n # Renvoie le nombre d'éléments ayant la valeur n dans Array

Nombre de tableaux {| i | i.even?} Renvoie le nombre en fonction de la condition invoquée sur chaque tableau d'éléments

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

Ici, les trois méthodes font le même travail. Cependant, voici où lecount devient intéressant.

Disons que je veux trouver combien d'éléments de tableau le tableau contient-il avec la valeur 2

array.count 2    # => 3

Le tableau a un total de trois éléments avec la valeur 2.

Maintenant, je veux trouver tous les éléments du tableau supérieurs à 4

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

Le tableau a un total de 6 éléments qui sont supérieurs à 4.

J'espère que cela donne des informations sur la countméthode.

Prabhakar Undurthi
la source