💾
Módulo #01

Collections e Enumerable

Descrição Collections são estruturas utilizadas para armazenar e manipular conjuntos de dados em Ruby. O módulo Enumerable fornece uma série de métodos que facilitam operações comuns sobre essas coleções, como percorrer, transformar, filtrar, buscar, ordenar, agrupar e reduzir elementos. Neste conteúdo, você aprenderá os fundamentos de Array, Hash e Range, além dos principais métodos do Enumerable, como each, map, select, find, reduce, sort, group_by e os métodos de verificação (any?, all?, none? e include?).

Collections e Enumerable

1. O que são Collections?

Collections são estruturas usadas para armazenar e organizar múltiplos valores. As principais collections nativas do Ruby são:

  • Array — coleção ordenada de elementos.
  • Hash — coleção de pares chave => valor.
  • Range — representa um intervalo de valores.
  • Set — coleção de valores únicos, disponível através da biblioteca set. Exemplo:
numbers = [1, 2, 3, 4, 5]

user = {
  name: "Tiago",
  age: 25
}

numbers_range = 1..5

2. Array

Um Array é uma coleção ordenada e indexada.

numbers = [10, 20, 30]

numbers[0] # 10
numbers[1] # 20
numbers[2] # 30

Os índices começam em 0.

Criando Arrays

numbers = [1, 2, 3]

names = ["Ana", "João", "Maria"]

mixed = [1, "Ruby", true, nil]

Adicionando elementos

numbers << 4

numbers.push(5)

numbers.unshift(0)

Resultado:

[0, 1, 2, 3, 4, 5]

Removendo elementos

numbers.pop
numbers.shift
  • pop remove o último elemento.
  • shift remove o primeiro elemento. Também é possível remover por índice:
numbers.delete_at(0)

Tamanho

numbers.length
numbers.size

Ambos retornam a quantidade de elementos.

Verificando elementos

numbers.include?(3)
# true

3. Hash

Hash armazena valores associados a chaves.

user = {
  name: "Tiago",
  age: 25,
  active: true
}

Acessando valores:

user[:name]
# "Tiago"

user[:age]
# 25

Adicionando ou alterando valores

user[:email] = "tiago@example.com"

user[:age] = 26

Removendo

user.delete(:email)

Verificando chaves

user.key?(:name)
# true

Verificando valores

user.value?("Tiago")
# true

Obtendo chaves e valores

user.keys
# [:name, :age, :active]

user.values
# ["Tiago", 25, true]

4. Range

Range representa uma sequência entre dois valores.

1..5

Inclui o 5:

(1..5).to_a
# [1, 2, 3, 4, 5]

Com ..., o último valor não é incluído:

(1...5).to_a
# [1, 2, 3, 4]

Ranges são bastante utilizados em iterações e verificações:

age = 25

(18..65).include?(age)
# true

5. O que é Enumerable?

Enumerable é um módulo do Ruby que fornece métodos para percorrer, transformar, filtrar, buscar e combinar elementos de uma coleção. Ele é utilizado por várias classes, principalmente:

  • Array
  • Hash
  • Range
  • Set Por exemplo:
[1, 2, 3].map { |number| number * 2 }

O Array possui os métodos do Enumerable. A ideia central é:

Uma coleção fornece uma forma de percorrer seus elementos através de each, e o Enumerable fornece diversas operações construídas sobre essa iteração.


6. each

each percorre todos os elementos de uma coleção.

numbers = [1, 2, 3]

numbers.each do |number|
  puts number
end

Também pode ser escrito com bloco curto:

numbers.each { |number| puts number }

O bloco recebe cada elemento da coleção.

Com Hash

user = {
  name: "Tiago",
  age: 25
}

user.each do |key, value|
  puts "#{key}: #{value}"
end

Retorno

each normalmente retorna a própria coleção:

result = [1, 2, 3].each { |number| number * 2 }

result
# [1, 2, 3]

each é utilizado principalmente quando queremos executar alguma ação para cada elemento, e não criar uma nova coleção.


7. map

map transforma cada elemento de uma coleção e retorna um novo Array.

numbers = [1, 2, 3]

numbers.map { |number| number * 2 }
# [2, 4, 6]

Outro exemplo:

names = ["ana", "joao", "maria"]

names.map { |name| name.upcase }
# ["ANA", "JOAO", "MARIA"]

A quantidade de elementos normalmente permanece a mesma. Uma forma simples de pensar:

map → "quero transformar cada elemento".


8. select

select retorna apenas os elementos que atendem a uma condição.

numbers = [1, 2, 3, 4, 5]

numbers.select { |number| number.even? }
# [2, 4]

Outro exemplo:

users = [
  { name: "Ana", active: true },
  { name: "João", active: false },
  { name: "Maria", active: true }
]

users.select { |user| user[:active] }

Resultado:

[
  { name: "Ana", active: true },
  { name: "Maria", active: true }
]

Uma forma simples de pensar:

select → "quero manter os elementos que atendem à condição".


9. reject

reject é o inverso de select. Ele remove os elementos que atendem à condição e mantém os demais.

numbers = [1, 2, 3, 4, 5]

numbers.reject { |number| number.even? }
# [1, 3, 5]

10. find

find retorna o primeiro elemento que atende à condição.

numbers = [1, 3, 4, 6, 8]

numbers.find { |number| number.even? }
# 4

Se nenhum elemento atender à condição:

numbers.find { |number| number > 10 }
# nil

Diferença importante:

numbers.select { |number| number.even? }
# [4, 6, 8]

numbers.find { |number| number.even? }
# 4
  • select → retorna vários elementos.
  • find → retorna o primeiro elemento encontrado.

11. any?, all? e none?

Esses métodos verificam condições sobre os elementos.

any?

Retorna true se pelo menos um elemento atender à condição.

numbers = [1, 2, 3, 4]

numbers.any? { |number| number.even? }
# true

all?

Retorna true se todos os elementos atenderem à condição.

numbers = [2, 4, 6]

numbers.all? { |number| number.even? }
# true

none?

Retorna true se nenhum elemento atender à condição.

numbers = [1, 3, 5]

numbers.none? { |number| number.even? }
# true

Uma forma simples de lembrar:

any?  → algum?
all?  → todos?
none? → nenhum?

12. include?

Verifica se uma coleção contém determinado elemento.

numbers = [1, 2, 3, 4]

numbers.include?(3)
# true

Com Hash, normalmente verificamos chaves ou valores explicitamente:

user.key?(:name)
user.value?("Tiago")

13. count

count retorna a quantidade de elementos.

numbers = [1, 2, 3, 4]

numbers.count
# 4

Também pode receber uma condição:

numbers.count { |number| number.even? }
# 2

14. reduce

reduce combina os elementos de uma coleção em um único resultado.

numbers = [1, 2, 3, 4]

numbers.reduce(0) do |total, number|
  total + number
end

# 10

O primeiro argumento (0) é o valor inicial do acumulador. Uma forma simplificada:

numbers.reduce(0, :+)
# 10

Outro exemplo:

numbers.reduce(1) do |total, number|
  total * number
end

# 24

Uma forma simples de pensar:

reduce → "quero transformar vários elementos em um único resultado".


15. sort e sort_by

sort ordena uma coleção.

numbers = [4, 1, 3, 2]

numbers.sort
# [1, 2, 3, 4]

Para objetos mais complexos, sort_by permite definir o critério de ordenação.

users = [
  { name: "Ana", age: 30 },
  { name: "João", age: 20 },
  { name: "Maria", age: 25 }
]

users.sort_by { |user| user[:age] }

Resultado:

[
  { name: "João", age: 20 },
  { name: "Maria", age: 25 },
  { name: "Ana", age: 30 }
]

16. group_by

group_by agrupa elementos de acordo com algum critério.

numbers = [1, 2, 3, 4, 5, 6]

numbers.group_by { |number| number.even? }

Resultado:

{
  false => [1, 3, 5],
  true => [2, 4, 6]
}

É especialmente útil quando precisamos transformar uma coleção em grupos.


17. select, map e each — diferença fundamental

Esses três métodos são muito importantes em Ruby.

each

Executa uma ação para cada elemento:

numbers.each do |number|
  puts number
end

map

Transforma os elementos:

numbers.map { |number| number * 2 }

select

Filtra os elementos:

numbers.select { |number| number.even? }

Uma regra prática:

each   → fazer algo
map    → transformar
select → filtrar

18. Encadeamento de métodos

Como os métodos do Enumerable retornam valores, podemos encadeá-los.

numbers = [1, 2, 3, 4, 5, 6]

numbers
  .select { |number| number.even? }
  .map { |number| number * 10 }

Resultado:

[20, 40, 60]

O processamento acontece da esquerda para a direita:

[1, 2, 3, 4, 5, 6]
        ↓
select
        ↓
[2, 4, 6]
        ↓
map
        ↓
[20, 40, 60]

19. Métodos com ! e mutação

Muitos métodos de collections retornam uma nova coleção sem modificar a original.

numbers = [1, 2, 3]

new_numbers = numbers.map { |number| number * 2 }

numbers
# [1, 2, 3]

new_numbers
# [2, 4, 6]

Alguns métodos possuem versões que modificam a própria coleção, geralmente indicadas por !.

numbers = [1, 2, 3]

numbers.map! { |number| number * 2 }

numbers
# [2, 4, 6]

É importante entender a diferença entre:

  • retornar uma nova coleção;
  • modificar a coleção existente.

20. Blocos e parâmetros

Os métodos do Enumerable utilizam bastante blocos.

numbers.map do |number|
  number * 2
end

number é o parâmetro recebido pelo bloco. Também podemos utilizar {}:

numbers.map { |number| number * 2 }

A escolha entre do...end e {} é principalmente uma questão de estilo e precedência de operadores.


21. Resumo dos principais métodos

MétodoObjetivoRetorno
eachPercorrer elementosColeção original
mapTransformar elementosNovo Array
selectFiltrar elementosNovo Array
rejectRemover elementos por condiçãoNovo Array
findEncontrar primeiro elementoElemento ou nil
any?Verificar se algum atendetrue/false
all?Verificar se todos atendemtrue/false
none?Verificar se nenhum atendetrue/false
include?Verificar existênciatrue/false
countContar elementosInteger
reduceCombinar elementosUm único valor
sortOrdenarNovo Array
sort_byOrdenar por critérioNovo Array
group_byAgrupar elementosHash