Arrays em Ruby: Um Guia Visual e Prático
Aprenda arrays em Ruby de forma visual: criar e ler, acrescentar e tirar nas duas pontas, pilhas e filas, each, map, select e companhia, ordenar, combinar, arrays aninhados, as pegadinhas, uma medição real de Array vs. Set e exercícios com respostas.

Nesta página
Uma playlist, um carrinho de compras, a tabela de recordes, os jogadores de uma partida, as linhas de um arquivo: os programas estão cheios de listas. Em Ruby, uma lista é um Array, e os arrays do Ruby estão entre os mais amigáveis de qualquer linguagem. Eles crescem e encolhem sozinhos, guardam qualquer coisa que você colocar e vêm com um conjunto enorme de métodos para buscar, ordenar e transformar.
Neste guia você vai ver como um array é organizado, acrescentar e tirar elementos nas duas pontas, montar uma pilha e uma fila, encadear iteradores como map e select em pipelines arrumados, ordenar e combinar arrays, escapar de duas pegadinhas clássicas e medir quando um array é a ferramenta errada. Exercícios com respostas fecham o artigo.
Todas as saídas abaixo vêm de execuções reais com o Ruby 3.3. Os exemplos usam p, que mostra os arrays com os colchetes e as aspas.
Um nome, muitas caixas
Em vez de cinco variáveis separadas para cinco animais, um array guarda todos sob um só nome, em ordem:
![Um array é uma prateleira numerada: um nome, muitas caixas. O array animais guarda "cão", "gato", "urso", "cavalo" e "águia" nos índices de 0 a 4, que também são de -5 a -1 contando a partir do fim. animais[1, 3] dá ["gato", "urso", "cavalo"]. animais.first é "cão", animais.last é "águia", animais.size é 5, e animais[10] é nil. Use fetch(10) se preferir receber um erro](https://nbkmundotech.us-southeast-1.linodeobjects.com/static/images/267dc4e7-7ae2-47c5-8620-900e27986e4f.png?v=1792858312729)
Criando arrays
1animais = ["cão", "gato", "urso", "cavalo", "águia"]
2misturado = [12, "cão", 3.65, nil, true, [1, 2]]
3palavras = %w[maçã banana cereja]
4zeros = Array.new(3, 0)
5quadrados = Array.new(5) { |indice| indice * indice }
6digitos = (1..5).to_a
7
8p animais
9p misturado
10p palavras
11p zeros
12p quadrados
13p digitos
14p [].empty?1["cão", "gato", "urso", "cavalo", "águia"]
2[12, "cão", 3.65, nil, true, [1, 2]]
3["maçã", "banana", "cereja"]
4[0, 0, 0]
5[0, 1, 4, 9, 16]
6[1, 2, 3, 4, 5]
7true- Colchetes com vírgulas são o jeito de sempre.
- Um array do Ruby pode misturar qualquer tipo de valor, até outros arrays.
%w[...]é um atalho para um array de palavras soltas.Array.new(5) { |indice| … }monta um array rodando o bloco uma vez por posição.
Lendo elementos
1animais = ["cão", "gato", "urso", "cavalo", "águia"]
2p animais[0]
3p animais[4]
4p animais[-1]
5p animais.first
6p animais.last(2)
7p animais[1, 3]
8p animais[1..3]
9p animais.size
10p animais[10]
11begin
12 animais.fetch(10)
13rescue IndexError => erro
14 puts "o fetch recusou: #{erro.message}"
15end1"cão"
2"águia"
3"águia"
4"cão"
5["cavalo", "águia"]
6["gato", "urso", "cavalo"]
7["gato", "urso", "cavalo"]
85
9nil
10o fetch recusou: index 10 outside of array bounds: -5...5A contagem começa no 0, então o último elemento fica em size - 1, ou simplesmente em -1. Pedir uma posição que não existe dá nil, em silêncio. Use o fetch quando um elemento faltando deve ser um erro.
Acrescentando e tirando elementos

1animais = ["cão", "gato", "urso"]
2
3animais.push("cavalo")
4animais << "águia"
5p animais
6
7tirado = animais.pop
8puts "o pop tirou #{tirado.inspect}"
9p animais
10
11animais.unshift("dinossauro")
12p animais
13
14primeiro = animais.shift
15puts "o shift tirou #{primeiro.inspect}"
16p animais1["cão", "gato", "urso", "cavalo", "águia"]
2o pop tirou "águia"
3["cão", "gato", "urso", "cavalo"]
4["dinossauro", "cão", "gato", "urso", "cavalo"]
5o shift tirou "dinossauro"
6["cão", "gato", "urso", "cavalo"]No meio
1animais = ["cão", "gato", "urso", "cavalo", "águia"]
2animais[1] = "tartaruga"
3p animais
4
5animais.insert(2, "coruja")
6p animais
7
8animais.delete("urso")
9p animais
10
11tirado = animais.delete_at(0)
12puts "o delete_at(0) tirou #{tirado.inspect}"
13p animais1["cão", "tartaruga", "urso", "cavalo", "águia"]
2["cão", "tartaruga", "coruja", "urso", "cavalo", "águia"]
3["cão", "tartaruga", "coruja", "cavalo", "águia"]
4o delete_at(0) tirou "cão"
5["tartaruga", "coruja", "cavalo", "águia"]O delete("urso") tira pelo valor (todas as cópias dele); o delete_at(0) tira pela posição.
Pilhas e filas
Esses quatro métodos das pontas transformam um array numa pilha ou numa fila prontas, dois dos padrões mais úteis da programação:

1# Uma pilha: o último a entrar é o primeiro a sair (como o desfazer)
2historico = []
3historico.push("digitar 'Olá'")
4historico.push("colocar negrito")
5historico.push("mudar a cor")
6puts "Desfazer: #{historico.pop}"
7puts "Desfazer: #{historico.pop}"
8p historico
9
10# Uma fila: o primeiro a entrar é o primeiro a sair (como a fila de uma loja)
11fila_de_impressao = []
12fila_de_impressao.push("relatorio.pdf")
13fila_de_impressao.push("foto.jpg")
14fila_de_impressao.push("ingresso.png")
15puts "Imprimindo: #{fila_de_impressao.shift}"
16puts "Imprimindo: #{fila_de_impressao.shift}"
17p fila_de_impressao1Desfazer: mudar a cor
2Desfazer: colocar negrito
3["digitar 'Olá'"]
4Imprimindo: relatorio.pdf
5Imprimindo: foto.jpg
6["ingresso.png"]Percorrendo um array
1animais = ["cão", "tartaruga", "urso"]
2
3animais.each { |animal| puts animal }
4
5animais.each_with_index do |animal, indice|
6 puts "#{indice + 1}. #{animal}"
7end
8
9indice = 0
10while indice < animais.length
11 puts animais[indice].upcase
12 indice += 1
13end1cão
2tartaruga
3urso
41. cão
52. tartaruga
63. urso
7CÃO
8TARTARUGA
9URSOO each entrega cada elemento, em ordem, ao bloco entre as chaves (ou entre do e end). O each_with_index acrescenta a posição. O laço while também funciona, mas em Ruby você quase sempre vai ver o each: não há índice para controlar e não tem como passar do fim.
A caixa de ferramentas dos iteradores: map, select e companhia
Além do each, os arrays têm iteradores que montam algo novo a partir dos elementos. Estes são os que você vai usar todo dia:

1precos = [12.5, 3.0, 45.0, 7.25, 19.99, 2.5]
2
3p precos.map { |preco| (preco * 1.1).round(2) }
4p precos.select { |preco| preco > 10 }
5p precos.reject { |preco| preco > 10 }
6p precos.find { |preco| preco > 10 }
7p precos.count { |preco| preco < 5 }
8p precos.sum
9p precos.min
10p precos.max
11p precos.sum / precos.size
12p precos.reduce { |total, preco| total + preco }1[13.75, 3.3, 49.5, 7.98, 21.99, 2.75]
2[12.5, 45.0, 19.99]
3[3.0, 7.25, 2.5]
412.5
52
690.24
72.5
845.0
915.04
1090.24| Método | Devolve | Pense nele como |
|---|---|---|
map | um array novo, do mesmo tamanho | "transforme cada um em…" |
select | os elementos que batem | "fique com os que…" |
reject | os elementos que não batem | "jogue fora os que…" |
find | o primeiro que bate (ou nil) | "ache um que…" |
count | quantos batem | "quantos são…" |
sum, min, max | um número | "some / o menor / o maior" |
reduce | um valor montado passo a passo | "combine todos em…" |
Encadeando num pipeline
Como cada um deles devolve um array novo, dá para encadeá-los, um passo por linha:

1pedidos = [
2 { item: "café", preco: 3.5, pago: true },
3 { item: "bolo", preco: 4.25, pago: false },
4 { item: "sanduíche", preco: 7.0, pago: true },
5 { item: "suco", preco: 4.0, pago: true },
6]
7
8total_pago = pedidos
9 .select { |pedido| pedido[:pago] }
10 .map { |pedido| pedido[:preco] }
11 .sum
12
13puts "Pago até agora: #{total_pago}"Pago até agora: 14.5Cada pedido aqui é um hash. Arrays de hashes são o jeito de costume de um programa Ruby guardar uma lista de registros.
Ordenando, contando e agrupando
1notas = [72, 95, 88, 61, 95, 90]
2nomes = ["eva", "Bruno", "ana", "Davi"]
3
4p notas.sort
5p notas.sort.reverse
6p notas.sort { |primeira, segunda| segunda <=> primeira }
7p notas.uniq
8p notas.max(3)
9p nomes.sort
10p nomes.sort_by(&:downcase)
11p nomes.max_by(&:length)
12p notas.tally
13p notas.group_by { |nota| nota >= 90 ? "A" : "não A" }1[61, 72, 88, 90, 95, 95]
2[95, 95, 90, 88, 72, 61]
3[95, 95, 90, 88, 72, 61]
4[72, 95, 88, 61, 90]
5[95, 95, 90]
6["Bruno", "Davi", "ana", "eva"]
7["ana", "Bruno", "Davi", "eva"]
8"Bruno"
9{72=>1, 95=>2, 88=>1, 61=>1, 90=>1}
10{"não A"=>[72, 88, 61], "A"=>[95, 95, 90]}- O
sortpuro coloca as maiúsculas antes das minúsculas ("Bruno"antes de"ana"). Osort_by(&:downcase)ordena do jeito que as pessoas esperam. - O
<=>é o operador "nave espacial", que compara dois valores. Trocar os dois lados ordena ao contrário. &:downcaseé um atalho para{ |nome| nome.downcase }.- O
tallyconta cada valor; ogroup_bysepara os elementos em grupos.
Combinando arrays

1meus = ["maçã", "banana", "cereja"]
2seus = ["banana", "cereja", "tâmara"]
3
4p meus + seus
5p meus - seus
6p meus & seus
7p meus | seus
8p [[1, 2], [3, [4, 5]]].flatten
9p ["Ana", "Bruno", "Carla"].zip([90, 85, 77])
10p (1..10).each_slice(3).to_a
11p [1, 2, 3, 4].each_cons(2).to_a1["maçã", "banana", "cereja", "banana", "cereja", "tâmara"]
2["maçã"]
3["banana", "cereja"]
4["maçã", "banana", "cereja", "tâmara"]
5[1, 2, 3, 4, 5]
6[["Ana", 90], ["Bruno", 85], ["Carla", 77]]
7[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
8[[1, 2], [2, 3], [3, 4]]O zip junta dois arrays em pares, como os dentes de um zíper; o each_slice(3) corta um array em grupos de 3; o each_cons(2) dá cada par de vizinhos.
Arrays dentro de arrays
Um array de arrays forma uma grade: linhas de colunas.
1tabuleiro = [
2 ["X", "O", "X"],
3 [" ", "X", "O"],
4 ["O", " ", "X"],
5]
6
7tabuleiro.each { |linha| puts linha.join(" | ") }
8p tabuleiro[1][2]
9p tabuleiro.map { |linha| linha[0] }
10p tabuleiro.transpose[0]
11diagonal = (0..2).map { |indice| tabuleiro[indice][indice] }
12puts "Diagonal: #{diagonal.join}"
13puts "O X venceu na diagonal!" if diagonal.all?("X")1X | O | X
2 | X | O
3O | | X
4"O"
5["X", " ", "O"]
6["X", " ", "O"]
7Diagonal: XXX
8O X venceu na diagonal!tabuleiro[1][2] é a linha 1, coluna 2. O transpose troca linhas por colunas, então tabuleiro.transpose[0] é a primeira coluna.
Duas pegadinhas
Pegadinha 1: Array.new(3, []) divide um único array interno
![Array.new(3, []) divide um único array interno. Com Array.new(3, []), as três casas apontam para o mesmo array, então grade[0] << "ops" aparece em todas as linhas. Com Array.new(3) { [] }, o bloco roda uma vez por casa, dando três arrays separados](https://nbkmundotech.us-southeast-1.linodeobjects.com/static/images/98b1533c-2f41-4488-8438-d7442817804a.png?v=1792858312884)
1grade = Array.new(3, [])
2grade[0] << "ops"
3p grade
4
5grade_segura = Array.new(3) { [] }
6grade_segura[0] << "certo"
7p grade_segura[["ops"], ["ops"], ["ops"]]
[["certo"], [], []]O Array.new(3, []) cria um array vazio e coloca ele nas três casas. Mudá-lo por uma casa muda "todas", porque elas são o mesmo array. A forma com bloco roda uma vez por casa e cria um array novinho a cada vez. (Números, como em Array.new(3, 0), não têm problema, porque não dá para mudar um número no lugar.)
Pegadinha 2: métodos com ! mudam o original
1numeros = [3, 1, 2]
2ordenados = numeros.sort
3p numeros
4p ordenados
5
6numeros.sort!
7p numeros
8
9dobrados = numeros.map { |numero| numero * 2 }
10p dobrados
11numeros.map! { |numero| numero * 10 }
12p numeros1[3, 1, 2]
2[1, 2, 3]
3[1, 2, 3]
4[2, 4, 6]
5[10, 20, 30]sort, map, select, reverse e uniq devolvem um array novo; os gêmeos com ! (sort!, map!, select!…) mudam o array no lugar. Métodos como push, pop, << e delete mudam o array mesmo sem !.
Quando o array é a ferramenta errada: buscar
O include? responde "isto está na lista?":
1require "set"
2
3convidados = ["Ana", "Bruno", "Carla"]
4p convidados.include?("Bruno")
5p convidados.include?("Zeca")
6p convidados.index("Carla")
7
8lista_de_convidados = Set.new(convidados)
9p lista_de_convidados.include?("Bruno")1true
2false
32
4trueMas, para responder, um array precisa olhar os elementos um por um. Um Set (conjunto) é feito exatamente para essa pergunta: ele pula direto para a resposta. Medimos 10.000 verificações contra listas cada vez maiores:

Com 100.000 nomes, o array precisou de 11,5 segundos; o Set, de 3,2 milissegundos: umas 3.600 vezes mais rápido. Cada vez que a lista cresceu 10×, o tempo do array também cresceu umas 10×, enquanto o Set quase nem percebeu.
Um mapa dos métodos de array do dia a dia
![Um mapa dos métodos de array do dia a dia. Criar: [1, 2, 3], %w[a b c], Array.new(3) com bloco, (1..5).to_a. Ler: [0] e [-1], first e last, [1, 3] e [1..3], size e empty?. Pôr e tirar: push e <<, pop, unshift e shift, insert e delete. Percorrer: each, each_with_index, each_slice, while. Transformar: map, select e reject, find, reduce. Medir: sum, min e max, count, tally. Ordenar: sort e sort_by, reverse, uniq, max_by. Combinar: + e -, & e |, zip, flatten](https://nbkmundotech.us-southeast-1.linodeobjects.com/static/images/fd9bdf54-b446-4fdd-ac2c-de8f5bf525b0.png?v=1792858312951)
Hora de praticar
Tente fazer estes antes de espiar as respostas.
Exercício 1: Adivinhe a saída (aquecimento). O que isto imprime?
1fila = [1, 2, 3]
2fila.push(4)
3fila.shift
4fila.unshift(0)
5fila.pop
6p filaExercício 2: Carrinho de compras. Dado um carrinho de itens com preço e quantidade, imprima cada linha com o subtotal e depois o total.
Exercício 3: Repetidos. Uma lista de inscrições tem repetições: ["ana", "bruno", "ana", "carla", "bruno", "ana", "davi"]. Imprima a lista sem repetidos (mantendo a ordem original), quantos repetidos foram tirados e quais nomes se inscreveram mais de uma vez.
Exercício 4: Times. Separe 10 jogadores em times de 3 (o último time fica com quem sobrar) e imprima cada time com o seu número.
Exercício 5: Ranking. Dados pares de [nome, pontos], imprima os 3 melhores, do maior para o menor, com as suas posições.
Exercício 6: Gire uma figura (desafio). Uma figurinha está guardada como linhas de caracteres. Gire-a 90° para a direita. (Dica: pense no que o transpose faz e depois no que ainda fica de trás para a frente.)
Quiz rápido
- Qual é o índice do último elemento de um array de 5 elementos? Dê dois jeitos de escrevê-lo.
- Que dupla de métodos forma uma pilha, e qual forma uma fila?
- Qual é a diferença entre
mapeeach? - Por que o
Array.new(3, [])surpreende as pessoas? - Quando você deve usar um
Setem vez de um array?
Respostas
Exercício 1: Passo a passo: [1, 2, 3, 4], depois o shift tira o 1, o unshift(0) coloca o 0 na frente e o pop tira o 4:
1fila = [1, 2, 3]
2fila.push(4)
3fila.shift
4fila.unshift(0)
5fila.pop
6p fila[0, 2, 3]Exercício 2: each para as linhas e sum com bloco para o total:
1carrinho = [
2 { item: "caderno", preco: 4.5, quantidade: 3 },
3 { item: "caneta", preco: 1.25, quantidade: 4 },
4 { item: "mochila", preco: 32.0, quantidade: 1 },
5]
6
7carrinho.each do |linha|
8 subtotal = linha[:preco] * linha[:quantidade]
9 puts "#{linha[:item].ljust(10)} #{linha[:quantidade]} x #{format('%5.2f', linha[:preco])} = #{format('%6.2f', subtotal)}"
10end
11total = carrinho.sum { |linha| linha[:preco] * linha[:quantidade] }
12puts "Total: #{format('%.2f', total)}"1caderno 3 x 4.50 = 13.50
2caneta 4 x 1.25 = 5.00
3mochila 1 x 32.00 = 32.00
4Total: 50.50Exercício 3: O uniq fica com a primeira cópia de cada nome, em ordem; o tally acha quem repetiu:
1inscricoes = ["ana", "bruno", "ana", "carla", "bruno", "ana", "davi"]
2sem_repetidos = inscricoes.uniq
3puts "Sem repetidos: #{sem_repetidos.inspect}"
4puts "Foram tirados #{inscricoes.size - sem_repetidos.size} repetidos"
5p inscricoes.tally.select { |nome, quantidade| quantidade > 1 }.keysSem repetidos: ["ana", "bruno", "carla", "davi"]
Foram tirados 3 repetidos
["ana", "bruno"]Exercício 4: O each_slice(3) monta os grupos, e o with_index(1) os numera a partir do 1:
1jogadores = %w[Ana Bruno Carla Davi Eva Fabio Gabi Hugo Iris Joao]
2jogadores.each_slice(3).with_index(1) do |time, numero|
3 puts "Time #{numero}: #{time.join(', ')}"
4end1Time 1: Ana, Bruno, Carla
2Time 2: Davi, Eva, Fabio
3Time 3: Gabi, Hugo, Iris
4Time 4: JoaoExercício 5: Ordene pelos pontos negativos para os maiores virem primeiro, pegue 3 e desmonte cada par no bloco com (nome, pontos):
1pontuacoes = [["Ana", 1200], ["Bruno", 950], ["Carla", 1430], ["Davi", 800], ["Eva", 1310]]
2
3tres_melhores = pontuacoes.sort_by { |nome, pontos| -pontos }.first(3)
4tres_melhores.each_with_index do |(nome, pontos), indice|
5 puts "#{indice + 1}. #{nome.ljust(6)} #{pontos}"
6end1. Carla 1430
2. Eva 1310
3. Ana 1200Exercício 6: O transpose transforma colunas em linhas; inverter cada uma dessas linhas completa o giro para a direita:
1figura = [
2 ["#", ".", "."],
3 ["#", "#", "."],
4 ["#", "#", "#"],
5]
6
7girada = figura.transpose.map(&:reverse)
8
9puts "Antes:"
10figura.each { |linha| puts linha.join }
11puts "Depois de girar para a direita:"
12girada.each { |linha| puts linha.join }1Antes:
2#..
3##.
4###
5Depois de girar para a direita:
6###
7##.
8#..Quiz: (1) O índice 4, que dá para escrever como array[4], array[-1] ou array.last. (2) Uma pilha usa push + pop (o último a entrar sai primeiro); uma fila usa push + shift (o primeiro a entrar sai primeiro). (3) O each só visita os elementos e devolve o array original; o map monta e devolve um array novo com o que o bloco devolver. (4) Ele coloca o mesmo array em todas as casas, então mudar um muda todos; use Array.new(3) { [] }. (5) Quando a pergunta principal é "isto está aqui?", principalmente em listas grandes: no nosso teste, um Set respondeu umas 3.600 vezes mais rápido com 100.000 nomes.
Cola rápida
| Eu quero… | Escreva |
|---|---|
| Criar um array | [1, 2, 3], %w[a b c], Array.new(3) { … }, (1..5).to_a |
| Ler um elemento | lista[0], lista[-1], lista.first, lista.last |
| Ler uma fatia | lista[1, 3], lista[1..3] |
| Contar elementos | lista.size, lista.count { … }, lista.empty? |
| Pôr no fim / na frente | lista.push(x) ou lista << x / lista.unshift(x) |
| Tirar do fim / da frente | lista.pop / lista.shift |
| Pôr / tirar no meio | lista.insert(2, x) / lista.delete(x), lista.delete_at(2) |
| Visitar cada elemento | lista.each { |item| … }, each_with_index |
| Transformar / filtrar | map { }, select { }, reject { }, find { } |
| Somar / extremos | sum, min, max, max_by { } |
| Ordenar | sort, sort_by { }, reverse |
| Tirar repetidos / contar valores | uniq / tally |
| Combinar | +, -, &, |, zip, flatten |
| Cortar em grupos | each_slice(3) |
| Conferir se algo está lá, rápido | require "set" e depois Set.new(lista).include?(x) |
Resumindo
Os arrays são a lista do dia a dia do Ruby, e eles fazem muita coisa:
- Os índices começam no 0, índices negativos contam a partir do fim, e um índice que não existe dá
nil(ou um erro comfetch). push/popeunshift/shifttrabalham nas duas pontas, o que deixa pilhas e filas triviais.- O
eachvisita;map,select,reject,find,sume companhia calculam, e se encadeiam em pipelines legíveis. sort,uniq,tally,group_by,zipe os operadores de conjunto respondem quase qualquer pergunta sobre uma lista numa linha só.- Cuidado com os valores compartilhados (
Array.new(3) { [] }, nãoArray.new(3, [])) e com os métodos que mudam o array no lugar. - Para "está aqui?" em listas grandes, use um
Set: umas 3.600 vezes mais rápido que um array com 100.000 nomes no nosso teste.
Para mais Ruby, leia O Que É a Linguagem de Programação Ruby?, Métodos de String em Ruby e Entrada e Saída de Arquivos em Ruby.
Leia a seguir

O Que É a Linguagem de Programação Rust? Um Guia Simples da Linguagem Rápida Que Não Deixa Você Travar
Um guia simples e sem jargões sobre o Rust, a linguagem tão rápida quanto o C que impede famílias inteiras de erros de memória antes de o programa rodar. Entenda a ideia de posse, por que gigantes da tecnologia o adotam e onde ele é usado.

O Que É a Linguagem de Programação Go? Um Guia Simples da Linguagem da Nuvem
Um guia simples e sem jargões sobre o Go, a linguagem simples e rápida que o Google criou para a nuvem. Entenda o que são goroutines, por que um programa Go é um arquivo só e como o Go passou a mover o Docker, o Kubernetes e boa parte da internet.

O Que É a Linguagem de Programação Ruby? Um Guia Simples da Linguagem Feita para a Felicidade
Um guia simples e sem jargões sobre o Ruby, a linguagem de programação feita para deixar os programadores felizes. Entenda por que o código Ruby parece inglês, como o Ruby on Rails move sites como GitHub e Shopify, e onde o Ruby brilha.
Comentários
Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.