Ruby do Zero: Arrays — Criação, Acesso, Transformação e Boas Práticas

Capítulo 9

Tempo estimado de leitura: 7 minutos

+ Exercício

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.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

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étodoO que fazOnde atua
push / <<adiciona elemento(s)final
popremove e retorna 1 elementofinal
unshiftadiciona elemento(s)início
shiftremove e retorna 1 elementoiní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 nil com compact.
  • Remover espaços com strip usando map.
  • 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 find para 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 reduce e acumulador inicial 0.
  • Encontrar o maior valor com reduce comparando.
  • 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: map em vez de construir array “na mão”.
  • Para filtrar: select/reject em vez de vários if espalhados.
  • Para agregar: reduce quando 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"]

Agora responda o exercício sobre o conteúdo:

Ao criar um array com Array.new usando um valor padrão mutável (como []), qual prática evita que o mesmo objeto seja compartilhado entre todas as posições?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Quando o valor padrão é mutável, ele pode ser reutilizado em todas as posições. Usar um bloco em Array.new cria um novo objeto a cada índice, evitando alterações refletidas em todo o array.

Próximo capitúlo

Ruby do Zero: Hashes — Chaves, Valores, Padrões e Manipulação Idiomática

Arrow Right Icon
Capa do Ebook gratuito Ruby do Zero: Fundamentos, Coleções, Blocos e Organização de Código
43%

Ruby do Zero: Fundamentos, Coleções, Blocos e Organização de Código

Novo curso

21 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.