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 pareschave => valor.Range— representa um intervalo de valores.Set— coleção de valores únicos, disponível através da bibliotecaset. 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
popremove o último elemento.shiftremove 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:
ArrayHashRangeSetPor 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 oEnumerablefornece 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étodo | Objetivo | Retorno |
|---|---|---|
each | Percorrer elementos | Coleção original |
map | Transformar elementos | Novo Array |
select | Filtrar elementos | Novo Array |
reject | Remover elementos por condição | Novo Array |
find | Encontrar primeiro elemento | Elemento ou nil |
any? | Verificar se algum atende | true/false |
all? | Verificar se todos atendem | true/false |
none? | Verificar se nenhum atende | true/false |
include? | Verificar existência | true/false |
count | Contar elementos | Integer |
reduce | Combinar elementos | Um único valor |
sort | Ordenar | Novo Array |
sort_by | Ordenar por critério | Novo Array |
group_by | Agrupar elementos | Hash |