O que é um Array e quando usar
Um Array é uma coleção ordenada de elementos. “Ordenada” significa que cada item tem uma posição (índice) e você pode acessar, substituir, remover e transformar valores com base nessa posição. Arrays são ideais quando você precisa manter uma lista (nomes, preços, notas, IDs) e aplicar operações em lote (filtrar, mapear, somar, buscar).
Criação de Arrays
Formas comuns de criar
vazio = []
nums = [10, 20, 30]
misto = ["Ana", 25, true, nil]
Você também pode criar com Array.new quando quer um tamanho inicial.
tres_zeros = Array.new(3, 0) # => [0, 0, 0]
Atenção: ao usar um objeto mutável como valor padrão, ele pode ser compartilhado entre posições. Prefira bloco:
# Problema: o mesmo array interno é reutilizado
matriz_ruim = Array.new(3, [])
matriz_ruim[0] << 1
# => [[1], [1], [1]]
# Correto: cria um novo array para cada posição
matriz_ok = Array.new(3) { [] }
matriz_ok[0] << 1
# => [[1], [], []]
Construção incremental
itens = []
itens << "mouse"
itens << "teclado"
# => ["mouse", "teclado"]
Acesso por índice (positivo e negativo)
Indexação positiva
O primeiro elemento está no índice 0.
nomes = ["Ana", "Bia", "Caio"]
nomes[0] # => "Ana"
nomes[2] # => "Caio"
nomes[10] # => nil (fora do intervalo)
Indexação negativa
Índices negativos contam a partir do final: -1 é o último.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
nomes[-1] # => "Caio"
nomes[-2] # => "Bia"
Leitura segura e intenção
Quando você quer expressar “primeiro” e “último”, prefira métodos que deixam a intenção clara:
nomes.first # => "Ana"
nomes.last # => "Caio"
Inserção e remoção: push/pop/shift/unshift
Essas operações são fundamentais para tratar arrays como filas e pilhas.
| Método | O que faz | Onde atua |
|---|---|---|
push / << | adiciona elemento(s) | final |
pop | remove e retorna 1 elemento | final |
unshift | adiciona elemento(s) | início |
shift | remove e retorna 1 elemento | início |
Exemplos práticos
fila = ["senha_001", "senha_002"]
fila.push("senha_003") # => ["senha_001", "senha_002", "senha_003"]
proxima = fila.shift # => "senha_001"
# fila agora => ["senha_002", "senha_003"]
pilha = []
pilha << "tarefa_a"
pilha << "tarefa_b"
ultima = pilha.pop # => "tarefa_b"
Passo a passo: simulando uma fila de atendimento
- Comece com uma lista de senhas.
- Quando chega alguém, adicione no final (
push). - Quando chama o próximo, remova do início (
shift).
senhas = ["A01", "A02"]
# chegou alguém
senhas.push("A03")
# chamou o próximo
atendido = senhas.shift
# estado atual
# atendido => "A01"
# senhas => ["A02", "A03"]
Slicing (fatias) e subarrays
“Slicing” é extrair uma parte do array. Você pode usar intervalo (.. ou ...) ou “início + quantidade”.
Com intervalos
nums = [10, 20, 30, 40, 50]
nums[1..3] # => [20, 30, 40] (inclui o 3)
nums[1...3] # => [20, 30] (exclui o 3)
nums[-3..-1] # => [30, 40, 50]
Com início e tamanho
nums[2, 2] # => [30, 40] (a partir do índice 2, pega 2 itens)
Extração com intenção: take e drop
nums.take(3) # => [10, 20, 30]
nums.drop(2) # => [30, 40, 50]
Transformações com blocos: métodos essenciais
Arrays ficam poderosos quando você combina métodos que recebem blocos. A ideia é: você descreve “o que fazer com cada elemento” e o Ruby aplica isso para você.
map: transformar cada elemento
Retorna um novo array com o resultado do bloco.
precos = [10, 20, 30]
com_taxa = precos.map { |p| p * 1.1 }
# => [11.0, 22.0, 33.0]
select e reject: filtrar
select mantém os que passam no critério; reject remove os que passam no critério.
nums = [1, 2, 3, 4, 5, 6]
pares = nums.select { |n| n.even? } # => [2, 4, 6]
impares = nums.reject { |n| n.even? } # => [1, 3, 5]
find: encontrar o primeiro que atende
nomes = ["ana", "bia", "caio"]
primeiro_com_a = nomes.find { |n| n.start_with?("a") }
# => "ana"
any? e all?: perguntas sobre o conjunto
idades = [18, 22, 16]
ha_menor = idades.any? { |i| i < 18 } # => true
so_maiores = idades.all? { |i| i >= 18 } # => false
reduce (ou inject): agregações
Acumula um valor ao longo do array (soma, produto, concatenação, máximo, etc.).
nums = [10, 20, 30]
soma = nums.reduce(0) { |acc, n| acc + n } # => 60
palavras = ["ru", "by"]
juntas = palavras.reduce("") { |acc, s| acc + s } # => "ruby"
Sem valor inicial, o primeiro elemento vira o acumulador:
[2, 3, 4].reduce { |acc, n| acc * n } # => 24
Composição de métodos (pipeline) e legibilidade
Você pode encadear transformações em etapas: normalizar → filtrar → transformar → agregar. A regra de ouro é manter cada etapa simples e previsível.
dados = [" Ana ", "BIA", "", " caio ", nil]
resultado = dados
.compact
.map { |s| s.strip }
.reject { |s| s.empty? }
.map { |s| s.downcase }
# => ["ana", "bia", "caio"]
Dica: se o encadeamento ficar longo, extraia partes para variáveis com nomes claros.
Exercícios práticos (com passo a passo)
Exercício 1 — Normalização de strings (limpeza de lista)
Objetivo: dado um array com nomes “sujos”, gerar uma lista padronizada: sem nil, sem vazios, sem espaços nas pontas, com capitalização consistente.
Entrada:
nomes = [" aNA ", nil, "", " bia", "CAIO ", " "]
Passo a passo:
- Remover
nilcomcompact. - Remover espaços com
stripusandomap. - Remover strings vazias com
reject. - Padronizar capitalização (ex.: primeira letra maiúscula) com
map.
normalizados = nomes
.compact
.map { |s| s.strip }
.reject { |s| s.empty? }
.map { |s| s.downcase }
.map { |s| s[0].upcase + s[1..] }
# => ["Ana", "Bia", "Caio"]
Variação: se você quiser manter tudo em minúsculas, remova a última etapa.
Exercício 2 — Filtragem por critérios (catálogo)
Objetivo: filtrar produtos elegíveis para frete grátis: preço mínimo e estoque disponível.
Entrada:
produtos = [
{ nome: "Cabo", preco: 19.9, estoque: 10 },
{ nome: "Mouse", preco: 99.9, estoque: 0 },
{ nome: "Teclado", preco: 149.0, estoque: 3 },
{ nome: "Suporte", preco: 39.9, estoque: 7 }
]
Passo a passo:
- Selecionar apenas itens com
estoque > 0. - Selecionar apenas itens com
preco >= 50. - Mapear para uma lista de nomes (ou outro formato).
elegiveis = produtos
.select { |p| p[:estoque] > 0 }
.select { |p| p[:preco] >= 50 }
.map { |p| p[:nome] }
# => ["Teclado"]
Variação: use reject para excluir itens sem estoque.
elegiveis = produtos
.reject { |p| p[:estoque] == 0 }
.select { |p| p[:preco] >= 50 }
.map { |p| p[:nome] }
Exercício 3 — Busca (find) e validações (any?/all?)
Objetivo: encontrar o primeiro pedido atrasado e verificar condições gerais.
pedidos = [
{ id: 1, status: "ok" },
{ id: 2, status: "ok" },
{ id: 3, status: "atrasado" },
{ id: 4, status: "ok" }
]
Passo a passo:
- Usar
findpara pegar o primeiro atrasado. - Usar
any?para saber se existe algum atrasado. - Usar
all?para saber se todos estão ok.
primeiro_atrasado = pedidos.find { |p| p[:status] == "atrasado" }
# => { id: 3, status: "atrasado" }
existe_atraso = pedidos.any? { |p| p[:status] == "atrasado" }
# => true
todos_ok = pedidos.all? { |p| p[:status] == "ok" }
# => false
Exercício 4 — Agregações com reduce (total, contagem e máximo)
Objetivo: calcular total de vendas e identificar o maior valor.
vendas = [120.0, 80.5, 300.0, 50.0]
Passo a passo:
- Somar tudo com
reducee acumulador inicial0. - Encontrar o maior valor com
reducecomparando. - Contar quantas vendas passaram de um limite.
total = vendas.reduce(0) { |acc, v| acc + v }
# => 550.5
maior = vendas.reduce { |acc, v| v > acc ? v : acc }
# => 300.0
acima_de_100 = vendas.select { |v| v > 100 }.length
# => 2
Boas práticas com Arrays
Prefira métodos expressivos a manipulação manual
- Para transformar:
mapem vez de construir array “na mão”. - Para filtrar:
select/rejectem vez de váriosifespalhados. - Para agregar:
reducequando houver um acumulador claro.
Evite mutação desnecessária
Muitos métodos têm versões “perigosas” (mutáveis) com ! (ex.: map!). Use quando você realmente quer alterar o array original e isso estiver claro no contexto.
nums = [1, 2, 3]
novos = nums.map { |n| n * 2 }
# nums => [1, 2, 3]
# novos => [2, 4, 6]
nums.map! { |n| n * 2 }
# nums => [2, 4, 6]
Cuidado com nil e dados incompletos
Se a lista pode conter nil, use compact antes de chamar métodos de string/número.
dados = ["ok", nil, " x "]
limpos = dados.compact.map { |s| s.strip }
Use slicing para evitar índices mágicos
Quando você precisa “pegar um pedaço”, prefira intervalos ou take/drop para deixar a intenção explícita.
ultimos_3 = nums[-3..]
primeiros_2 = nums.take(2)
Composição com clareza
Encadeie métodos quando cada etapa for simples. Se ficar difícil de ler, quebre em variáveis intermediárias.
brutos = [" A ", "", " b ", nil]
sem_nil = brutos.compact
sem_espaco = sem_nil.map { |s| s.strip }
validos = sem_espaco.reject { |s| s.empty? }
# => ["A", "b"]