Size, Length e Count: Qual a diferença?

No Ruby, vira e mexe a gente se depara com métodos que parecem fazer a mesma coisa. Alguns são só apelidos (aliases), mas outros têm diferenças que podem dar um nó na cabeça — principalmente quando o Rails entra na jogada com o ActiveRecord.

No Ruby puro

O length é o mais comum. Ele te diz quantos elementos tem num array, numa string ou num hash. Ele conta o que já está ali na memória e pronto.

  array = [1, 2, 3, 4, 5]
  puts array.length # => 5

  string = "Hello, World!"
  puts string.length # => 13

Já o size é basicamente a mesma coisa que o length para arrays e strings. A escolha entre um ou outro é mais questão de gosto ou contexto: eu costumo usar length para strings e size para coleções, mas o resultado é o mesmo.

O count já é um pouco diferente. Além de contar o total, você pode passar um argumento ou um bloco para filtrar o que quer contar.

  numeros = [1, 2, 3, 4, 5, 6]
  
  # Quantos são pares?
  numeros.count(&:even?) # => 3
  
  # Quantas vezes aparece o número 2?
  numeros.count(2) # => 1

No Rails a coisa muda

Quando entramos no contexto do ActiveRecord, a escolha entre esses métodos impacta diretamente a performance, já que cada um fala com o banco de dados de um jeito diferente.

O count vai sempre fazer uma query SELECT COUNT(*) no banco. Ele é útil quando você quer o valor exato que está lá no banco naquele momento, ignorando qualquer alteração que você tenha feito no objeto mas ainda não salvou. Só é bom evitar chamar ele várias vezes seguidas, senão vira uma chuva de queries desnecessárias.

Já o length traz todo mundo para a memória. Ele carrega os registros do banco, transforma em objetos Ruby e só depois conta o tamanho do array. Se você já carregou os dados antes ou sabe que vai precisar usar eles logo em seguida, o length é uma boa. O problema é se a tabela for gigante: carregar milhares de linhas na memória só para saber o total vai ser um gargalo enorme.

O size acaba sendo o meio termo ideal. Ele é “esperto”: se os dados já estão carregados, ele só conta o que está na memória (como o length). Se não estão, ele faz o COUNT(*) no banco (como o count).

Na prática, eu acabo usando o size quase sempre. Ele resolve a maioria dos casos de forma eficiente sem que eu precise ficar conferindo se os dados já foram carregados ou não.