Dia 8 — Arrays

Informatica · Conteudo · publicado em 02/10/2026
Aula 1

Criar, acessar e alterar arrays

Criar e alterar array: push, pop, splice

Array é a lista ordenada: valores guardados por posição, numerada a partir de 0, e o tamanho mora em .length. A forma literal é [], e length é contagem de elementos — não é o maior índice. Uma lista de três itens tem length 3 e último índice 2.

O primeiro elemento é o índice 0 e o último é length - 1. Array vazio é [], com length 0 — e pop, shift e at em lista vazia devolvem undefined, sem erro.

const frutas = ['maçã', 'banana', 'laranja'];

frutas[0]        // 'maçã' — o primeiro elemento
frutas[2]        // 'laranja' — o último elemento, length - 1
frutas.at(-1)    // 'laranja' — o mesmo, contando do fim
frutas.length    // 3

const vazia = [];
vazia.pop()      // undefined — não quebra

Índice fora da lista devolve undefined e não quebra o programa. Atribuir em índice maior que o length cria buraco: [1, , 3], que tem length 3 mas só dois elementos — o do meio não existe. Esse array se chama sparse: os buracos não são undefined, são posições vazias, e método como forEach pula por cima delas em vez de passar.

MétodoAssinaturaEfeito
pushpush(...itens): numberacrescenta no fim, devolve o novo length
poppop()remove e devolve o último
shiftshift()remove e devolve o primeiro
unshiftunshift(...itens): numberinsere no início, devolve o novo length
splicesplice(inicio, quantos, ...novos): arrayremove e insere em qualquer ponto
atat(indice)acessa contando do fim: at(-1) é o último
concatconcat(...outros): arraydevolve lista nova com as duas juntas
joinjoin(separador): stringtransforma a lista em texto

As duas pontas custam caro

push e pop trabalham no fim: o JavaScript não precisa mover nada. shift e unshift trabalham no início: todo elemento é deslocado uma posição, e em lista grande isso pesa. unshift paga mais ainda, porque o motor precisa abrir espaço para o novo item e só depois preenchê-lo. Quando o custo importa, use push e deixe a ordem para o fim.

splice é o único dos sete que aceita o índice do meio, e é o único dos quatro de alterar que devolve os elementos removidos, não os inseridos.

const letras = ['a', 'b', 'c', 'd'];
letras.splice(1, 2, 'X');   // ['a', 'X', 'd']

const não protege o conteúdo

const impede reatribuar a variável, não mexer dentro dela. Lista declarada com const pode ganhar e perder elementos à vontade; o que não compila é frutas = [...]. A mesma regra vale para objeto, e o congelamento de verdade é Object.freeze (dia 9).

Os verbos do dia são adicionar e remover, e cada ponta da lista tem o seu par: push e pop no fim, unshift e shift no início, e splice em qualquer ponto.

const notas = [8, 9];
notas.push(10);   // permitido
// notas = [];    // TypeError: Assignment to constant variable

Cópia de verdade é [...notas] ou notas.slice(): os dois criam lista nova. Atribuir const copia = notas não copia nada — as duas variáveis apontam para a mesma lista, e mexer em uma muda a outra.

length é a única propriedade do array que é gravável, e escrever nela trunca ou estende: notas.length = 1 joga fora tudo depois do primeiro item; notas.length = 3 cria buracos, com undefined no lugar do que não foi preenchido. Nenhum dos dois avisa.

Exemplo

'use strict';
// Exemplo: criar, ler e alterar um array, e onde cada metodo custa caro.

const frutas = ['maçã', 'banana', 'laranja'];

console.log('lista:', frutas.join(', '));
console.log('length e 3 e o ultimo indice e 2:', frutas.length, frutas.length - 1);
console.log('indice 0:', frutas[0], '| indice 2:', frutas[2]);

// Indice fora da lista devolve undefined e nao quebra nada.
console.log('indice 99 (nao existe):', frutas[99]);

// Atribuir em indice grande cria buraco: o elemento do meio nao existe.
const esparsa = [1, , 3];
console.log('esparsa.length:', esparsa.length, '| esparsa:', esparsa.map((v) => String(v)).join(' '));

// As duas pontas: push e pop sao baratos, shift e unshift remexem em tudo.
const itens = ['b', 'c'];
itens.push('d');
console.log('depois de push:', itens.join(', '), '| length', itens.length);
console.log('pop devolveu:', itens.pop(), '| sobrou:', itens.join(', '));
console.log('unshift devolve o novo length:', itens.unshift('a'), '| lista:', itens.join(', '));
console.log('shift devolveu:', itens.shift(), '| sobrou:', itens.join(', '));

// splice mexe no meio e devolve o que REMOVEU, nao o que inseriu.
const letras = ['a', 'b', 'c', 'd'];
const removidas = letras.splice(1, 2, 'X');
console.log('splice(1, 2, "X") ->', letras.join(', '), '| devolveu as removidas:', removidas.join(', '));

// splice com quantos 0 so insere, sem apagar ninguem.
const numeros = [1, 2, 3];
numeros.splice(1, 0, 1.5);
console.log('splice(1, 0, 1.5) ->', numeros.join(', '));

// at() conta a partir do fim; o indice -1 nao existe de outra forma.
console.log('at(-1) e o ultimo:', letras.at(-1), '| at(0):', letras.at(0));

// const trava a variavel, nao o conteudo. E o push passa.
const notas = [8, 9];
notas.push(10);
console.log('const com push:', notas.join(', '));
try {
  notas = [];
} catch (erro) {
  console.log(erro.constructor.name + ': ' + erro.message);
}

// Copia de verdade: spread ou slice. Atribuicao nao copia.
const original = [1, 2, 3];
const copia = [...original];
copia.push(4);
console.log('original intacto:', original.join(', '), '| copia:', copia.join(', '));

const mesma = original;
mesma.push(99);
console.log('as duas apontam para a mesma lista:', original.join(', '));

// length e gravavel: escrever nela trunca ou preenche com buraco.
const trunca = [1, 2, 3, 4, 5];
trunca.length = 2;
console.log('length = 2 trunca:', trunca.join(', '));

const estende = [1];
estende.length = 3;
console.log('length = 3 estende:', estende.map((v) => String(v)).join(', '));

// Array de uma linha, sem const, ainda e um array.
console.log('literal direto:', ['a', 'b'].join('-'));

// concat devolve lista nova, sem mexer nas originais.
const a = [1, 2];
const b = [3, 4];
const juntas = a.concat(b, [5]);
console.log('concat:', juntas.join(', '), '| a continua', a.length, '| b continua', b.length);

Saída real

lista: maçã, banana, laranja
length e 3 e o ultimo indice e 2: 3 2
indice 0: maçã | indice 2: laranja
indice 99 (nao existe): undefined
esparsa.length: 3 | esparsa: 1  3
depois de push: b, c, d | length 3
pop devolveu: d | sobrou: b, c
unshift devolve o novo length: 3 | lista: a, b, c
shift devolveu: a | sobrou: b, c
splice(1, 2, "X") -> a, X, d | devolveu as removidas: b, c
splice(1, 0, 1.5) -> 1, 1.5, 2, 3
at(-1) e o ultimo: d | at(0): a
const com push: 8, 9, 10
TypeError: Assignment to constant variable.
original intacto: 1, 2, 3 | copia: 1, 2, 3, 4
as duas apontam para a mesma lista: 1, 2, 3, 99
length = 2 trunca: 1, 2
length = 3 estende: 1, , 
literal direto: a-b
concat: 1, 2, 3, 4, 5 | a continua 2 | b continua 2
Aula 2

Iterar, transformar e filtrar arrays

map, filter e reduce: transformar e filtrar array

O for...of percorre; estes métodos devolvem algo. É a diferença que decide o código: quando o método entrega resultado novo, encadeia; quando só serve para efeito colateral, o retorno é undefined e o resultado se joga fora.

MétodoAssinaturaDevolve
forEachforEach(callback): undefinednada, só executa
mapmap(callback): arrayarray do mesmo tamanho, com o valor trocado
filterfilter(callback): arraysó os que passaram no teste
findfind(callback)o primeiro que passou, ou undefined
findIndexfindIndex(callback): numbero índice do primeiro, ou -1
somesome(callback): booleantrue se algum passou
everyevery(callback): booleantrue se todos passaram
reducereduce(callback, inicial): qualquerum valor só, acumulado
sortsort(comparador): arraya mesma lista, ordenada
reversereverse(): arraya mesma lista, ao contrário
sliceslice(inicio, fim): arraytrecho, em lista nova
flatflat(profundidade): arrayachata listas dentro de listas

Os dois que devolvem array do mesmo tamanho

map e filter recebem o mesmo tipo de callback e são fáceis de confundir. A regra é o nome: map transforma cada elemento e mantém a quantidade; filter escolhe elementos e muda a quantidade.

const numeros = [1, 2, 3, 4];

numeros.map((n) => n * 10);      // [10, 20, 30, 40]  — 4 itens
numeros.filter((n) => n > 2);   // [3, 4]            — 2 itens

map sobre objeto devolve array de valores, sem a chave: quem precisa do par usa Object.entries(obj).map(([chave, valor]) => ...).

reduce é o resto com dois parâmetros

O callback de reduce recebe o acumulado e o item: (acumulado, item) => novoAcumulado, e devolve o novo acumulado. O segundo argumento de reduce é o valor inicial.

const notas = [8, 9, 7];
notas.reduce((soma, nota) => soma + nota, 0);   // 24

Sem valor inicial, o acumulado começa no primeiro elemento — daí a necessidade do 0 ao somar lista de número. Com número a soma ainda sai certa por sorte; com objeto, o primeiro registro se mistura no resultado, porque o acumulado já começou sendo o próprio item e não um objeto vazio. Com 0, '', [] ou {} como segundo argumento, a base é limpa, e a soma de array vazio devolve 0 em vez de explodir.

reduceRight faz o mesmo da direita para a esquerda. Serve para cortar texto em dobras, ou para não estourar a pilha quando o array é enorme e a operação cria array novo a cada passo.

Juntar, cortar e copiar

concat junta: a.concat(b) devolve lista nova com as duas, e a.concat(b, c) aceita várias de uma vez. Não altera nenhuma das duas. Para juntar várias listas de uma vez, o caminho curto é o spread em array — concat(...listas), ou o que o material chama de ...array, que é [...a, ...b]: o mesmo operador do dia 7 com outro papel.

splice (dia 8, aula 1) altera a lista original, tirando e inserindo; slice é o que só entrega um trecho, em lista nova, sem tocar no original. A cópia de array de verdade é [...lista] ou lista.slice(): os dois criam lista nova, e atribuir const copia = lista não copia nada — as duas variáveis apontam para a mesma lista.

const a = [1, 2];
const b = [3, 4];

a.concat(b);          // [1, 2, 3, 4]
[...a, ...b];         // [1, 2, 3, 4] — o mesmo resultado
a.slice(0, 1);        // [1] — trecho, a lista segue com 2 itens

Ordenar sem quebrar

sort com um único argumento ordena como texto: [10, 9, 1].sort() devolve [1, 10, 9]. Com números, sempre comparador: (a, b) => a - b para crescente, (b - a) => b - a para decrescente. O comparador devolve um número — negativo, zero ou positivo — e o sort reordena a lista original.

Para não mexer no original, o caminho curto é [...lista].sort(...) ou lista.slice().sort(...).

find devolve o elemento e findIndex devolve o índice. Trocar os dois troca o resultado inteiro do código, porque objeto é o que o find devolve e 0 é o que o findIndex devolve para o primeiro item — e 0 é falsy (dia 5), então if (lista.findIndex(...)) falha justamente no achado. Use !== -1, ou some, que devolve boolean.

Exemplo

'use strict';
// Exemplo: os metodos que devolvem array, os que devolvem valor e os
// que so executam. A diferenca e o que separa map de filter.

const numeros = [1, 2, 3, 4, 5];

// forEach executa e devolve undefined: o retorno se joga fora.
console.log('forEach devolve:', numeros.forEach((n) => n * 2));

// map transforma cada elemento e MANTEM a quantidade.
console.log('map (5 itens):', numeros.map((n) => n * 10).join(', '));

// filter escolhe elementos e MUDA a quantidade.
console.log('filter (2 itens):', numeros.filter((n) => n > 3).join(', '));

// Encadeamento: cada um alimenta o seguinte, porque cada um devolve array.
const dobradosPares = numeros
  .map((n) => n * 2)
  .filter((n) => n % 4 === 0);
console.log('map + filter encadeados:', dobradosPares.join(', '));

// find devolve o ELEMENTO; findIndex devolve o INDICE.
const notas = [
  { aluno: 'Ana', nota: 6 },
  { aluno: 'Bia', nota: 9 },
  { aluno: 'Cau', nota: 7 },
];
const reprovado = notas.find((n) => n.nota < 7);
const indiceReprovado = notas.findIndex((n) => n.nota < 7);
console.log('find devolve o objeto:', reprovado.aluno, reprovado.nota);
console.log('findIndex devolve o indice:', indiceReprovado, 'ou -1:', notas.findIndex((n) => n.nota > 100));

// A armadilha: o indice 0 e falsy, entao o if abaixo nao entra.
if (notas.findIndex((n) => n.nota < 7)) {
  console.log('isto nao imprime: achou no indice 0, que e falsy');
}
if (notas.findIndex((n) => n.nota < 7) !== -1) {
  console.log('isto imprime: a comparacao com -1 e a forma correta');
}
console.log('some devolve boolean, sem esse problema:', notas.some((n) => n.nota < 7));

// some responde "existe algum"; every responde "todos".
console.log('some (>=7):', notas.some((n) => n.nota >= 7), '| some (>=10):', notas.some((n) => n.nota >= 10));
console.log('every (>=6):', notas.every((n) => n.nota >= 6), '| every (>=8):', notas.every((n) => n.nota >= 8));

// reduce junta tudo num valor so. O segundo argumento e o valor inicial.
console.log('reduce soma:', numeros.reduce((soma, n) => soma + n, 0));

// Sem valor inicial, o acumulado comeca no PRIMEIRO ELEMENTO: comeca sendo
// o proprio registro, nao um objeto vazio. O que o codigo escreve nele se
// acumula em cima da chave que ja existia.
const sujo = notas.reduce((acc, n) => { acc[n.aluna] = n.nota; return acc; });
console.log('reduce sem inicial: o registro original sobrevive no resultado ->');
console.log('  aluno:', sujo.aluno, '| nota:', sujo.nota, '| [undefined]:', sujo[undefined]);

console.log('reduce com objeto, base {} limpa:', notas.reduce((acc, n) => {
  acc[n.aluno] = n.nota;
  return acc;
}, {}));

// reduceRight faz a mesma coisa de tras para frente.
const palavras = ['e', 'a', 'volta'];
console.log('reduceRight:', palavras.reduceRight((acc, p) => acc + p + ' ', '').trim());

// sort sem comparador ordena como texto, e 10 vem antes de 9.
const embaralhado = [10, 9, 1, 100];
console.log('sort sem comparador:', embaralhado.slice().sort().join(', '));
console.log('sort com (a, b) => a - b:', embaralhado.slice().sort((a, b) => a - b).join(', '));
console.log('sort decrescente:', embaralhado.slice().sort((a, b) => b - a).join(', '));

// reverse e slice mexem na lista, mas slice devolve uma nova.
const letras = ['a', 'b', 'c', 'd'];
const copia = letras.slice(1, 3);
copia.reverse();
console.log('slice(1, 3):', copia.join(', '), '| o original intacto:', letras.join(', '));

// flat achata listas dentro de listas; flatMap achata um nivel so.
const agrupado = [[1, 2], [3, 4]];
console.log('flat:', agrupado.flat().join(', '));
console.log('flat(1):', [[1, [2]], 3].flat(1).join(', '));
console.log('flatMap:', agrupado.flatMap((par) => par).join(', '));

// A tabela de uma linha que o professor sempre pede: nome e media.
const alunos = [
  { nome: 'Ana', notas: [8, 9, 7] },
  { nome: 'Bia', notas: [5, 6, 7] },
  { nome: 'Cau', notas: [4, 3, 9] },
];
const relatorio = alunos
  .map((aluno) => ({
    nome: aluno.nome,
    media: +(aluno.notas.reduce((s, n) => s + n, 0) / aluno.notas.length).toFixed(2),
  }))
  .filter((aluno) => aluno.media >= 6);
console.log('aprovados:', relatorio.map((a) => a.nome + ' (' + a.media + ')').join(', '));
console.log('quantos aprovaram:', relatorio.length, 'de', alunos.length);

Saída real

forEach devolve: undefined
map (5 itens): 10, 20, 30, 40, 50
filter (2 itens): 4, 5
map + filter encadeados: 4, 8
find devolve o objeto: Ana 6
findIndex devolve o indice: 0 ou -1: -1
isto imprime: a comparacao com -1 e a forma correta
some devolve boolean, sem esse problema: true
some (>=7): true | some (>=10): false
every (>=6): true | every (>=8): false
reduce soma: 15
reduce sem inicial: o registro original sobrevive no resultado ->
  aluno: Ana | nota: 6 | [undefined]: 7
reduce com objeto, base {} limpa: { Ana: 6, Bia: 9, Cau: 7 }
reduceRight: volta a e
sort sem comparador: 1, 10, 100, 9
sort com (a, b) => a - b: 1, 9, 10, 100
sort decrescente: 100, 10, 9, 1
slice(1, 3): c, b | o original intacto: a, b, c, d
flat: 1, 2, 3, 4
flat(1): 1, 2, 3
flatMap: 1, 2, 3, 4
aprovados: Ana (8), Bia (6)
quantos aprovaram: 2 de 3