Cuenta, tamaño, longitud ... ¿demasiadas opciones en Ruby?

140

Parece que no puedo encontrar una respuesta definitiva sobre esto y quiero asegurarme de que entiendo esto al "nivel n" :-)

    a = {"a" => "Hola", "b" => "Mundo"}
    una cuenta # 2
    a. tamaño # 2
    a. longitud # 2

    a = [10, 20]
    una cuenta # 2
    a. tamaño # 2
    a. longitud # 2

Entonces, ¿cuál usar? Si quiero saber si un tiene más de un elemento, entonces no parece importar, pero quiero asegurarme de que entiendo la diferencia real. Esto también se aplica a las matrices. Obtuve los mismos resultados.

Además, me doy cuenta de que cuenta / tamaño / longitud tienen diferentes significados con ActiveRecord. En este momento, estoy principalmente interesado en Ruby puro (1.92), pero si alguien quiere intervenir en la diferencia, AR también lo agradecería.

¡Gracias!

cbmeeks
fuente
55
El fenómeno que ha encontrado a veces se llama TMTOWTDI : hay más de una forma de hacerlo. Este eslogan proviene de la comunidad Perl, y Perl es una de las influencias en Ruby.
Andrew Grimm
Por lo general, estos son alias entre ellos, hacen lo mismo. Hay un método que también debe tener en cuenta: Array#nitemsque devuelve el número de elementos que no son NIL en una matriz. Pero eso ya no está disponible en Ruby 1.9
Tilo el

Respuestas:

194

Para matrices y hashes sizees un alias para length. Son sinónimos y hacen exactamente lo mismo.

count es más versátil: puede tomar un elemento o predicado y contar solo los elementos que coinciden.

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

En el caso de que no proporcione un parámetro para contar, básicamente tiene el mismo efecto que la longitud de la llamada. Sin embargo, puede haber una diferencia de rendimiento.

Podemos ver en el código fuente de Array que hacen casi exactamente lo mismo. Aquí está el código C para la implementación de array.length:

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

Y aquí está la parte relevante de la implementación 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..
    }
}

El código para array.counthace algunas comprobaciones adicionales, pero al final llama exactamente el mismo código: LONG2NUM(RARRAY_LEN(ary)).

Los hashes ( código fuente ), por otro lado, no parecen implementar su propia versión optimizada, por countlo que se usa la implementación de Enumerable( código fuente ), que itera sobre todos los elementos y los cuenta uno por uno.

En general, recomendaría usar length(o su alias size) en lugar de countsi desea saber cuántos elementos hay en total.


En cuanto a ActiveRecord, por el contrario, no son diferencias importantes. mira esta publicación:

Mark Byers
fuente
10

Hay una diferencia crucial para las aplicaciones que utilizan conexiones de base de datos.

Cuando utiliza muchos ORM (ActiveRecord, DataMapper, etc.), el entendimiento general es que .size generará una consulta que solicite todos los elementos de la base de datos ('select * from mytable') y luego le dará la cantidad de elementos resultante, mientras que .count generará una sola consulta ('select count (*) from mytable') que es considerablemente más rápido.

Debido a que estos ORM son tan frecuentes, sigo el principio de menor asombro. En general, si ya tengo algo en la memoria, entonces uso .size, y si mi código generará una solicitud a una base de datos (o servicio externo a través de una API), uso .count.

stef
fuente
1
Algo a considerar con esto es counter_cache. Si tiene una tabla, fooy tiene_muchas bar, tendrá una columna con foonombre bars_countque se actualiza cada vez que barse crea / destruye una. El uso foo.bars.sizees lo que comprueba esa columna (sin consultar realmente ninguna bars). foo.bars.countrealiza la consulta real, lo que anularía el propósito del caché.
Dudo
7

En la mayoría de los casos (por ejemplo, Array o String ) sizees un alias para length.

countnormalmente proviene de Enumerable y puede tomar un bloque de predicados opcional. Así enumerable.count {cond}es [aproximadamente] (enumerable.select {cond}).length: por supuesto, puede pasar por alto la estructura intermedia, ya que solo necesita el recuento de predicados coincidentes.

Nota: No estoy seguro si count fuerza una evaluación de la enumeración si el bloque no está especificado o si hace un cortocircuito en el lengthcaso de que sea posible.

Editar (y gracias a la respuesta de Mark!): count Sin un bloque (al menos para las matrices) no obliga a una evaluación. Supongo que sin un comportamiento formal está "abierto" para otras implementaciones, si forzar una evaluación sin un predicado realmente tiene sentido de todos modos.


fuente
5

Encontré un buen answare en http://blog.hasmanythrough.com/2008/2/27/count-length-size

En ActiveRecord, hay varias formas de averiguar cuántos registros hay en una asociación, y hay algunas diferencias sutiles en su funcionamiento.

post.comments.count: determina el número de elementos con una consulta SQL COUNT. También puede especificar condiciones para contar solo un subconjunto de los elementos asociados (por ejemplo: condiciones => {: nombre_autor => "josh"}). Si configura un caché de contador en la asociación, #count devolverá ese valor almacenado en caché en lugar de ejecutar una nueva consulta.

post.comments.length: siempre carga el contenido de la asociación en la memoria y luego devuelve el número de elementos cargados. Tenga en cuenta que esto no forzará una actualización si la asociación se había cargado previamente y luego se crearon nuevos comentarios de otra manera (por ejemplo, Comment.create (...) en lugar de post.comments.create (...)).

post.comments.size: funciona como una combinación de las dos opciones anteriores. Si la colección ya se ha cargado, devolverá su longitud al igual que llamar a #length. Si aún no se ha cargado, es como llamar a #count.

También tengo una experiencia personal:

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

Tenemos varias formas de averiguar cuántos elementos de una matriz .length, como .county .size. Sin embargo, es mejor usar en array.sizelugar de array.count. Porque .sizees mejor en rendimiento.

Venkat M
fuente
1

Añadiendo más a la respuesta de Mark Byers. En Ruby, el método array.sizees un alias del método Array # length . No hay diferencia técnica en el uso de ninguno de estos dos métodos. Posiblemente no verá ninguna diferencia en el rendimiento también. Sin embargo, array.counttambién hace el mismo trabajo pero con algunas funcionalidades adicionales Array # count

Se puede usar para obtener el número total de elementos en función de alguna condición. Se puede llamar a Count de tres maneras:

Array # count # Devuelve el número de elementos en Array

Array # count n # Devuelve el número de elementos que tienen valor n en Array

Array # count {| i | i.even?} Devuelve el recuento basado en la condición invocada en cada matriz de elementos

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

Aquí los tres métodos hacen el mismo trabajo. Sin embargo, aquí es donde se countpone interesante.

Digamos, quiero encontrar cuántos elementos de matriz contiene la matriz con el valor 2

array.count 2    # => 3

La matriz tiene un total de tres elementos con un valor de 2.

Ahora, quiero encontrar todos los elementos de matriz mayores que 4

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

La matriz tiene un total de 6 elementos que son> que 4.

Espero que brinde información sobre el countmétodo.

Prabhakar Undurthi
fuente