Contagem, tamanho, comprimento ... muitas opções em Ruby?

140

Não consigo encontrar uma resposta definitiva sobre isso e quero ter certeza de que entendo isso no "nésimo nível" :-)

    a = {"a" => "Olá", "b" => "Mundo"}
    a.contagem # 2
    a.size # 2
    a.length # 2

    a = [10, 20]
    a.contagem # 2
    a.size # 2
    a.length # 2

Então, qual usar? Se eu quiser saber se a possui mais de um elemento, isso não parece importar, mas quero ter certeza de que entendi a diferença real. Isso se aplica a matrizes também. Eu obtenho os mesmos resultados.

Além disso, percebo que contagem / tamanho / comprimento têm significados diferentes com o ActiveRecord. Estou interessado principalmente em Ruby puro (1,92) agora, mas se alguém quiser conversar sobre a diferença que o AR faz, isso também seria apreciado.

Obrigado!

cbmeeks
fonte
5
O fenômeno que você encontrou às vezes é chamado TMTOWTDI : há mais de uma maneira de fazê-lo. Esse slogan vem da comunidade Perl, e Perl é uma das influências no Ruby.
Andrew Grimm
esses geralmente são apelidos um para o outro - eles fazem o mesmo. Há um método que você também deve ter em mente Array#nitems:, que retorna o número de itens que não são NIL em uma matriz. Mas isso não está mais disponível no Ruby 1.9
2020 Tilo

Respostas:

194

Para matrizes e hashes sizeé um alias para length. Eles são sinônimos e fazem exatamente a mesma coisa.

count é mais versátil - ele pode pegar um elemento ou predicado e contar apenas os itens correspondentes.

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

No caso em que você não fornece um parâmetro para contar, ele tem basicamente o mesmo efeito que a duração da chamada. Pode haver uma diferença de desempenho.

Podemos ver no código-fonte do Array que eles fazem quase exatamente a mesma coisa. Aqui está o código C para a implementação de array.length:

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

E aqui está a parte relevante da implementação 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..
    }
}

O código para array.countfaz algumas verificações extras, mas no final pede exatamente o mesmo código: LONG2NUM(RARRAY_LEN(ary)).

Os hashes ( código fonte ), por outro lado, não parecem implementar sua própria versão otimizada, de countmodo que a implementação de Enumerable( código fonte ) é usada, que itera sobre todos os elementos e os conta um por um.

Em geral, eu recomendaria usar length(ou seu apelido size) em vez de countse você quiser saber quantos elementos existem no total.


Em relação ActiveRecord, por outro lado, não são diferenças importantes. confira este post:

Mark Byers
fonte
10

Há uma diferença crucial para aplicativos que fazem uso de conexões com o banco de dados.

Quando você está usando muitos ORMs (ActiveRecord, DataMapper etc.), o entendimento geral é que .size gerará uma consulta que solicita todos os itens do banco de dados ('select * from mytable') e fornece o número de itens resultante, enquanto .count gerará uma única consulta ('select count (*) from mytable') que é consideravelmente mais rápida.

Como essas ORMs são tão predominantes, eu sigo o princípio do mínimo espanto. Em geral, se já tenho algo na memória, uso .size e, se meu código gerar uma solicitação para um banco de dados (ou serviço externo por meio de uma API), uso .count.

stef
fonte
1
Algo a considerar com isso é counter_cache. Se tiver uma tabela, fooe tem has_many bar, você terá uma coluna foonomeada bars_countque será atualizada sempre que uma barcriação / destruição for criada. Usar foo.bars.sizeé o que verifica essa coluna (sem realmente consultar nenhuma bars). foo.bars.countfaz a consulta real, o que anularia a finalidade do cache.
Dudo
7

Na maioria dos casos (por exemplo, Array ou String ) sizeé um alias para length.

countnormalmente vem de Enumerable e pode receber um bloco de predicado opcional. Assim enumerable.count {cond}é [aproximadamente] (enumerable.select {cond}).length- é claro que pode ignorar a estrutura intermediária, pois precisa apenas da contagem de predicados correspondentes.

Nota: Não tenho certeza se count força uma avaliação da enumeração se o bloco não estiver especificado ou se houver um curto-circuito no lengthpossível.

Editar (e graças à resposta de Mark!): count Sem um bloco (pelo menos para Arrays) não força uma avaliação. Suponho que, sem um comportamento formal, esteja "aberto" para outras implementações, se forçar uma avaliação sem um predicado realmente faz sentido de qualquer maneira.


fonte
5

Encontrei um bom answare em http://blog.hasmanythrough.com/2008/2/27/count-length-size

No ActiveRecord, existem várias maneiras de descobrir quantos registros existem em uma associação e existem algumas diferenças sutis no modo como eles funcionam.

post.comments.count - determine o número de elementos com uma consulta SQL COUNT. Você também pode especificar condições para contar apenas um subconjunto dos elementos associados (por exemplo: conditions => {: author_name => "josh"}). Se você configurar um cache de contador na associação, #count retornará esse valor em cache em vez de executar uma nova consulta.

post.comments.length - sempre carrega o conteúdo da associação na memória e retorna o número de elementos carregados. Observe que isso não forçará uma atualização se a associação tiver sido carregada anteriormente e novos comentários foram criados de outra maneira (por exemplo, Comment.create (...) em vez de post.comments.create (...)).

post.comments.size - funciona como uma combinação das duas opções anteriores. Se a coleção já tiver sido carregada, ela retornará seu comprimento, assim como chamar #length. Se ainda não foi carregado, é como chamar #count.

Também tenho uma experiência pessoal:

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

Temos várias maneiras de descobrir quantos elementos em uma matriz como .length, .counte .size. No entanto, é melhor usar array.sizedo que array.count. Porque .sizeé melhor no desempenho.

Venkat M
fonte
1

Adicionando mais à resposta de Mark Byers. Em Ruby, o método array.sizeé um alias para o método Array # length . Não há diferença técnica no uso de qualquer um desses dois métodos. Possivelmente você não verá nenhuma diferença no desempenho também. No entanto, o array.counttambém faz o mesmo trabalho, mas com algumas funcionalidades extras Array # count

Pode ser usado para obter o número total de elementos com base em alguma condição. A contagem pode ser chamada de três maneiras:

Array # count # Retorna o número de elementos na Array

Matriz # count n # Retorna o número de elementos com o valor n na Matriz

Matriz # count {| i | i.even?} Retorna a contagem com base na condição chamada em cada matriz de elemento

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

Aqui todos os três métodos fazem o mesmo trabalho. No entanto, aqui é onde countfica interessante.

Digamos, quero descobrir quantos elementos da matriz a matriz contém com o valor 2

array.count 2    # => 3

A matriz possui um total de três elementos com o valor 2.

Agora, quero encontrar todos os elementos da matriz maiores que 4

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

A matriz possui um total de 6 elementos>> 4.

Espero que ele dê algumas informações sobre o countmétodo.

Prabhakar Undurthi
fonte