Matrizes fornecem muitos métodos. Para facilitar as coisas, neste capítulo eles estão divididos em grupos.
Já conhecemos métodos que adicionam e removem itens do início ou do final:
arr.push(...items)
– adiciona itens ao final,
arr.pop()
– extrai um item do final,
arr.shift()
– extrai um item desde o início,
arr.unshift(...items)
– adiciona itens ao início.
Aqui estão alguns outros.
Como deletar um elemento do array?
Os arrays são objetos, então podemos tentar usar delete
:
deixe arr = ["eu", "ir", "casa"]; excluir arr[1]; //remove "ir" alerta(arr[1]); // indefinido // agora arr = ["I", , "home"]; alerta(arr.length); //3
O elemento foi removido, mas o array ainda possui 3 elementos, podemos ver que arr.length == 3
.
Isso é natural, porque delete obj.key
remove um valor de key
. É tudo o que faz. Ótimo para objetos. Mas para arrays geralmente queremos que o restante dos elementos se desloque e ocupe o lugar liberado. Esperamos ter um array mais curto agora.
Portanto, métodos especiais devem ser utilizados.
O método arr.splice é um canivete suíço para matrizes. Pode fazer tudo: inserir, remover e substituir elementos.
A sintaxe é:
arr.splice(iniciar[, deleteCount, elem1, ..., elemN])
Ele modifica arr
a partir do índice start
: remove os elementos deleteCount
e depois insere elem1, ..., elemN
em seu lugar. Retorna a matriz de elementos removidos.
Este método é fácil de entender por meio de exemplos.
Vamos começar com a exclusão:
deixe arr = ["Eu", "estudo", "JavaScript"]; arr.splice(1, 1); // do índice 1 remove 1 elemento alerta(arr); // ["Eu", "JavaScript"]
Fácil, certo? A partir do índice 1
removeu 1
elemento.
No próximo exemplo, removemos 3 elementos e os substituímos pelos outros dois:
deixe arr = ["Eu", "estudo", "JavaScript", "certo", "agora"]; // remove os 3 primeiros elementos e os substitui por outro arr.splice(0, 3, "Vamos", "dança"); alert( arr ) // agora ["Vamos", "dança", "certo", "agora"]
Aqui podemos ver que splice
retorna o array de elementos removidos:
deixe arr = ["Eu", "estudo", "JavaScript", "certo", "agora"]; //remove os 2 primeiros elementos deixe removido = arr.splice(0, 2); alerta(removido); // "I", "estudo" <- array de elementos removidos
O método splice
também é capaz de inserir os elementos sem remoções. Para isso, precisamos definir deleteCount
como 0
:
deixe arr = ["Eu", "estudo", "JavaScript"]; //do índice 2 //exclui 0 // depois insira "complexo" e "linguagem" arr.splice(2, 0, "complexo", "linguagem"); alerta(arr); // "Eu", "estudo", "complexo", "linguagem", "JavaScript"
Índices negativos permitidos
Aqui e em outros métodos de array, índices negativos são permitidos. Eles especificam a posição do final do array, como aqui:
deixe arr = [1, 2, 5]; // do índice -1 (um passo do final) //exclui 0 elementos, // depois insere 3 e 4 arr.splice(-1, 0, 3, 4); alerta(arr); //1,2,3,4,5
O método arr.slice é muito mais simples que o arr.splice
de aparência semelhante.
A sintaxe é:
arr.slice([início], [fim])
Ele retorna um novo array copiando para ele todos os itens do start
ao end
do índice (não incluindo end
). Tanto start
quanto end
podem ser negativos; nesse caso, a posição do final da matriz é assumida.
É semelhante a um método de string str.slice
, mas em vez de substrings, ele cria submatrizes.
Por exemplo:
deixe arr = ["t", "e", "s", "t"]; alerta( arr.slice(1, 3) ); // e,s (copiar de 1 a 3) alerta( arr.slice(-2) ); // s,t (copia de -2 até o final)
Também podemos chamá-lo sem argumentos: arr.slice()
cria uma cópia de arr
. Isso geralmente é usado para obter uma cópia para transformações adicionais que não devem afetar o array original.
O método arr.concat cria um novo array que inclui valores de outros arrays e itens adicionais.
A sintaxe é:
arr.concat(arg1, arg2...)
Ele aceita qualquer número de argumentos – matrizes ou valores.
O resultado é um novo array contendo itens de arr
, depois arg1
, arg2
etc.
Se um argumento argN
for um array, todos os seus elementos serão copiados. Caso contrário, o próprio argumento será copiado.
Por exemplo:
deixe arr = [1, 2]; // cria um array de: arr e [3,4] alerta( arr.concat([3, 4]) ); //1,2,3,4 // cria um array de: arr e [3,4] e [5,6] alerta( arr.concat([3, 4], [5, 6]) ); //1,2,3,4,5,6 // cria um array de: arr e [3,4], depois adiciona os valores 5 e 6 alerta( arr.concat([3, 4], 5, 6) ); //1,2,3,4,5,6
Normalmente, ele apenas copia elementos de arrays. Outros objetos, mesmo que pareçam arrays, são adicionados como um todo:
deixe arr = [1, 2]; deixe arrayLike = { 0: "alguma coisa", comprimento: 1 }; alerta(arr.concat(arrayLike) ); // 1,2,[objeto Objeto]
…Mas se um objeto semelhante a um array tiver uma propriedade especial Symbol.isConcatSpreadable
, então ele será tratado como um array por concat
: seus elementos serão adicionados em seu lugar:
deixe arr = [1, 2]; deixe arrayLike = { 0: "alguma coisa", 1: "outro", [Symbol.isConcatSpreadable]: verdadeiro, comprimento: 2 }; alerta(arr.concat(arrayLike) ); // 1,2, alguma coisa, outro
O método arr.forEach permite executar uma função para cada elemento do array.
A sintaxe:
arr.forEach(function(item, índice, array) { // ... faça algo com um item });
Por exemplo, isso mostra cada elemento do array:
// para cada alerta de chamada de elemento ["Bilbo", "Gandalf", "Nazgul"].forEach(alert);
E este código é mais elaborado sobre suas posições no array alvo:
["Bilbo", "Gandalf", "Nazgul"].forEach((item, índice, array) => { alert(`${item} está no índice ${index} em ${array}`); });
O resultado da função (se retornar algum) é descartado e ignorado.
Agora vamos abordar os métodos que pesquisam em um array.
Os métodos arr.indexOf e arr.includes têm sintaxe semelhante e fazem essencialmente o mesmo que suas contrapartes de string, mas operam em itens em vez de caracteres:
arr.indexOf(item, from)
– procura item
começando no índice from
e retorna o índice onde foi encontrado, caso contrário -1
.
arr.includes(item, from)
– procura item
começando no índice from
e retorna true
se encontrado.
Normalmente, esses métodos são usados com apenas um argumento: o item
a ser pesquisado. Por padrão, a pesquisa é desde o início.
Por exemplo:
deixe arr = [1, 0, falso]; alerta( arr.indexOf(0) ); //1 alerta(arr.indexOf(falso)); //2 alerta( arr.indexOf(null) ); // -1 alerta( arr.includes(1) ); // verdadeiro
Observe que indexOf
usa a igualdade estrita ===
para comparação. Então, se procurarmos false
, encontra exatamente false
e não o zero.
Se quisermos verificar se item
existe no array e não precisamos do índice, então arr.includes
é o preferido.
O método arr.lastIndexOf é igual ao indexOf
, mas procura da direita para a esquerda.
deixe frutas = ['Maçã', 'Laranja', 'Maçã'] alerta(frutas.indexOf('Apple') ); // 0 (primeira Apple) alerta(frutas.lastIndexOf('Apple') ); // 2 (última maçã)
O método includes
trata NaN
corretamente
Um recurso menor, mas digno de nota, de includes
é que ele lida corretamente com NaN
, diferentemente de indexOf
:
const arr = [NaN]; alerta( arr.indexOf(NaN) ); // -1 (errado, deveria ser 0) alert( arr.includes(NaN) );// verdadeiro (correto)
Isso ocorre porque includes
foi adicionado ao JavaScript muito mais tarde e usa internamente o algoritmo de comparação mais atualizado.
Imagine que temos uma variedade de objetos. Como encontramos um objeto com uma condição específica?
Aqui o método arr.find(fn) é útil.
A sintaxe é:
deixe resultado = arr.find(function(item, índice, array) { // se true for retornado, o item será retornado e a iteração será interrompida // para cenário falso retorna indefinido });
A função é chamada para elementos do array, um após o outro:
item
é o elemento.
index
é o seu índice.
array
é o próprio array.
Se retornar true
, a pesquisa é interrompida e o item
é retornado. Se nada for encontrado, undefined
será retornado.
Por exemplo, temos um array de usuários, cada um com os campos id
e name
. Vamos encontrar aquele com id == 1
:
deixe os usuários = [ {id: 1, nome: "João"}, {id: 2, nome: "Pete"}, {id: 3, nome: "Maria"} ]; deixe usuário = usuários.find(item => item.id == 1); alerta(usuário.nome); // John
Na vida real, arrays de objetos são comuns, então o método find
é muito útil.
Observe que no exemplo fornecemos para find
a função item => item.id == 1
com um argumento. Isso é típico, outros argumentos desta função raramente são usados.
O método arr.findIndex tem a mesma sintaxe, mas retorna o índice onde o elemento foi encontrado em vez do próprio elemento. O valor -1
será retornado se nada for encontrado.
O método arr.findLastIndex é como findIndex
, mas pesquisa da direita para a esquerda, semelhante a lastIndexOf
.
Aqui está um exemplo:
deixe os usuários = [ {id: 1, nome: "João"}, {id: 2, nome: "Pete"}, {id: 3, nome: "Maria"}, {id: 4, nome: "João"} ]; //Encontre o índice do primeiro John alert(users.findIndex(user => user.name == 'John')); //0 //Encontre o índice do último John alert(users.findLastIndex(user => user.name == 'John')); //3
O método find
procura um único (primeiro) elemento que faz a função retornar true
.
Se houver muitos, podemos usar arr.filter(fn).
A sintaxe é semelhante a find
, mas filter
retorna um array de todos os elementos correspondentes:
deixe resultados = arr.filter(function(item, index, array) { // se o item verdadeiro for enviado para os resultados e a iteração continuar // retorna array vazio se nada for encontrado });
Por exemplo:
deixe os usuários = [ {id: 1, nome: "João"}, {id: 2, nome: "Pete"}, {id: 3, nome: "Maria"} ]; // retorna array dos dois primeiros usuários deixe alguns usuários = usuários.filter (item => item.id <3); alerta(algunsUsuários.length); //2
Vamos passar para os métodos que transformam e reordenam um array.
O método arr.map é um dos mais úteis e frequentemente usados.
Ele chama a função para cada elemento do array e retorna o array de resultados.
A sintaxe é:
deixe resultado = arr.map(function(item, índice, array) { // retorna o novo valor em vez do item });
Por exemplo, aqui transformamos cada elemento em seu comprimento:
deixe comprimentos = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length); alerta(comprimentos); //5,7,6
A chamada para arr.sort() classifica o array in place , alterando a ordem dos elementos.
Ele também retorna o array classificado, mas o valor retornado geralmente é ignorado, pois o próprio arr
é modificado.
Por exemplo:
deixe arr = [1, 2, 15]; // o método reordena o conteúdo de arr arr.sort(); alerta(arr); //1, 15, 2
Você notou algo estranho no resultado?
A ordem passou a ser 1, 15, 2
. Incorreto. Mas por que?
Os itens são classificados como strings por padrão.
Literalmente, todos os elementos são convertidos em strings para comparações. Para strings, a ordem lexicográfica é aplicada e, de fato, "2" > "15"
.
Para usar nossa própria ordem de classificação, precisamos fornecer uma função como argumento de arr.sort()
.
A função deve comparar dois valores arbitrários e retornar:
função comparar(a, b) { se (a > b) retornar 1; // se o primeiro valor for maior que o segundo se (a == b) retornar 0; //se os valores forem iguais se (a <b) retornar -1; // se o primeiro valor for menor que o segundo }
Por exemplo, para classificar como números:
função compararNumeric(a, b) { se (a > b) retornar 1; se (a == b) retornar 0; se (a <b) retornar -1; } deixe arr = [1, 2, 15]; arr.sort(compareNumeric); alerta(arr); //1, 2, 15
Agora funciona como pretendido.
Vamos nos afastar e pensar no que está acontecendo. O arr
pode ser um array de qualquer coisa, certo? Pode conter números ou strings ou objetos ou qualquer outra coisa. Temos um conjunto de alguns itens . Para classificá-lo, precisamos de uma função ordenadora que saiba comparar seus elementos. O padrão é uma ordem de string.
O método arr.sort(fn)
implementa um algoritmo de classificação genérico. Não precisamos nos preocupar com o funcionamento interno (um quicksort otimizado ou Timsort na maioria das vezes). Ele percorrerá o array, comparará seus elementos usando a função fornecida e os reordenará, tudo o que precisamos é fornecer o fn
que faz a comparação.
Aliás, se algum dia quisermos saber quais elementos são comparados – nada nos impede de alertá-los:
[1, -2, 15, 2, 0, 8].sort(função(a, b) { alerta(a + " <> " + b ); retorne a - b; });
O algoritmo pode comparar um elemento com vários outros no processo, mas tenta fazer o mínimo de comparações possível.
Uma função de comparação pode retornar qualquer número
Na verdade, uma função de comparação só é necessária para retornar um número positivo para dizer “maior” e um número negativo para dizer “menos”.
Isso permite escrever funções mais curtas:
deixe arr = [1, 2, 15]; arr.sort(function(a, b) { return a - b; }); alerta(arr); //1, 2, 15
Funções de seta para o melhor
Lembra das funções das setas? Podemos usá-los aqui para uma classificação mais organizada:
arr.sort( (a, b) => a - b );
Isso funciona exatamente da mesma forma que a versão mais longa acima.
Use localeCompare
para strings
Lembra do algoritmo de comparação de strings? Ele compara as letras por seus códigos por padrão.
Para muitos alfabetos, é melhor usar o método str.localeCompare
para classificar letras corretamente, como Ö
.
Por exemplo, vamos classificar alguns países em alemão:
deixe países = ['Österreich', 'Andorra', 'Vietnã']; alert(países.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnã, Österreich (errado) alert(países.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnã (correto!)
O método arr.reverse inverte a ordem dos elementos em arr
.
Por exemplo:
seja arr = [1, 2, 3, 4, 5]; arr.reverse(); alerta(arr); //5,4,3,2,1
Ele também retorna o array arr
após a reversão.
Aqui está a situação da vida real. Estamos escrevendo um aplicativo de mensagens e a pessoa entra na lista de destinatários delimitada por vírgulas: John, Pete, Mary
. Mas para nós um array de nomes seria muito mais confortável do que uma única string. Como conseguir isso?
O método str.split(delim) faz exatamente isso. Ele divide a string em uma matriz pelo delimitador fornecido delim
.
No exemplo abaixo, dividimos por vírgula seguida de espaço:
deixe nomes = 'Bilbo, Gandalf, Nazgul'; deixe arr = nomes.split(', '); for (deixe o nome do arr) { alert( `Uma mensagem para ${nome}.` ); // Uma mensagem para Bilbo (e outros nomes) }
O método split
possui um segundo argumento numérico opcional – um limite no comprimento do array. Se for fornecido, os elementos extras serão ignorados. Na prática, raramente é usado:
deixe arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2); alerta(arr); //Bilbo, Gandalf
Dividido em letras
A chamada para split(s)
com um s
vazio dividiria a string em um array de letras:
deixe str = "teste"; alerta(str.split('') ); // teste
A chamada arr.join(glue) faz o inverso para split
. Ele cria uma sequência de itens arr
unidos por glue
entre eles.
Por exemplo:
deixe arr = ['Bilbo', 'Gandalf', 'Nazgul']; deixe str = arr.join(';'); // cola o array em uma string usando ; alerta(str); // Bilbo;Gandalf;Nazgul
Quando precisamos iterar sobre um array – podemos usar forEach
, for
ou for..of
.
Quando precisarmos iterar e retornar os dados de cada elemento – podemos usar map
.
Os métodos arr.reduce e arr.reduceRight também pertencem a essa raça, mas são um pouco mais complexos. Eles são usados para calcular um único valor com base na matriz.
A sintaxe é:
deixe valor = arr.reduce(function(acumulador, item, índice, array) { // ... }, [inicial]);
A função é aplicada a todos os elementos do array, um após o outro, e “transporta” seu resultado para a próxima chamada.
Argumentos:
accumulator
– é o resultado da chamada de função anterior, igual initial
na primeira vez (se initial
for fornecido).
item
– é o item atual da matriz.
index
– é a sua posição.
array
- é a matriz.
À medida que a função é aplicada, o resultado da chamada de função anterior é passado para a próxima como primeiro argumento.
Assim, o primeiro argumento é essencialmente o acumulador que armazena o resultado combinado de todas as execuções anteriores. E no final, torna-se o resultado de reduce
.
Parece complicado?
A maneira mais fácil de entender isso é pelo exemplo.
Aqui obtemos a soma de um array em uma linha:
seja arr = [1, 2, 3, 4, 5]; deixe resultado = arr.reduce((soma, atual) => soma + atual, 0); alerta(resultado); // 15
A função passada para reduce
usa apenas 2 argumentos, o que normalmente é suficiente.
Vamos ver os detalhes do que está acontecendo.
Na primeira execução, sum
é o valor initial
(o último argumento de reduce
), é igual a 0
e current
é o primeiro elemento da matriz, é igual 1
. Portanto, o resultado da função é 1
.
Na segunda execução, sum = 1
, adicionamos o segundo elemento do array ( 2
) a ele e retornamos.
Na 3ª execução, sum = 3
e adicionamos mais um elemento a ela, e assim por diante…
O fluxo de cálculo:
Ou na forma de uma tabela, onde cada linha representa uma chamada de função no próximo elemento do array:
sum | current | resultado | |
---|---|---|---|
a primeira chamada | 0 | 1 | 1 |
a segunda chamada | 1 | 2 | 3 |
a terceira chamada | 3 | 3 | 6 |
a quarta chamada | 6 | 4 | 10 |
a quinta chamada | 10 | 5 | 15 |
Aqui podemos ver claramente como o resultado da chamada anterior se torna o primeiro argumento da próxima.
Também podemos omitir o valor inicial:
seja arr = [1, 2, 3, 4, 5]; // removeu o valor inicial de reduzir (não 0) deixe resultado = arr.reduce((soma, atual) => soma + atual); alerta(resultado); // 15
O resultado é o mesmo. Isso porque se não houver inicial, reduce
pega o primeiro elemento do array como valor inicial e inicia a iteração a partir do segundo elemento.
A tabela de cálculo é igual à anterior, menos a primeira linha.
Mas tal utilização exige extremo cuidado. Se a matriz estiver vazia, reduce
a chamada sem valor inicial gera um erro.
Aqui está um exemplo:
deixe arr = []; // Erro: Redução de array vazio sem valor inicial // se o valor inicial existisse, reduzir o retornaria para o arr vazio. arr.reduce((soma, atual) => soma + atual);
Portanto, é aconselhável sempre especificar o valor inicial.
O método arr.reduceRight faz o mesmo, mas vai da direita para a esquerda.
Matrizes não formam um tipo de linguagem separado. Eles são baseados em objetos.
Então typeof
não ajuda a distinguir um objeto simples de um array:
alerta(tipo de {}); // objeto alerta(tipo de []); // objeto (mesmo)
…Mas arrays são usados com tanta frequência que existe um método especial para isso: Array.isArray(valor). Retorna true
se o value
for uma matriz e false
caso contrário.
alerta(Array.isArray({})); // falso alerta(Array.isArray([])); // verdadeiro
Quase todos os métodos de array que chamam funções – como find
, filter
, map
, com uma exceção notável de sort
, aceitam um parâmetro adicional opcional thisArg
.
Esse parâmetro não é explicado nas seções acima porque raramente é usado. Mas para completar, temos que cobri-lo.
Aqui está a sintaxe completa desses métodos:
arr.find(func, thisArg); arr.filter(func, thisArg); arr.map(func, thisArg); // ... // thisArg é o último argumento opcional
O valor do parâmetro thisArg
torna-se this
para func
.
Por exemplo, aqui usamos um método de objeto army
como filtro, e thisArg
passa o contexto:
deixe exército = { Idade mínima: 18, idade máxima: 27, canJoin(usuário) { return user.age >= this.minAge && user.age < this.maxAge; } }; deixe os usuários = [ {idade: 16}, {idade: 20}, {idade: 23}, {idade: 30} ]; // encontra usuários, para quem army.canJoin retorna verdadeiro deixe soldados = usuários.filter(army.canJoin, exército); alerta(soldados.comprimento); //2 alerta(soldados[0].idade); //20 alerta(soldados[1].idade); //23
Se no exemplo acima usássemos users.filter(army.canJoin)
, então army.canJoin
seria chamado como uma função autônoma, com this=undefined
, levando a um erro instantâneo.
Uma chamada para users.filter(army.canJoin, army)
pode ser substituída por users.filter(user => army.canJoin(user))
, que faz o mesmo. Este último é usado com mais frequência, pois é um pouco mais fácil de entender para a maioria das pessoas.
Uma folha de dicas de métodos de array:
Para adicionar/remover elementos:
push(...items)
– adiciona itens ao final,
pop()
– extrai um item do final,
shift()
– extrai um item desde o início,
unshift(...items)
– adiciona itens ao início.
splice(pos, deleteCount, ...items)
– no índice pos
exclui elementos deleteCount
e insere items
.
slice(start, end)
– cria um novo array, copia elementos do start
ao end
do índice (não incluído) nele.
concat(...items)
– retorna um novo array: copia todos os membros do array atual e adiciona items
a ele. Se algum dos items
for um array, então seus elementos serão obtidos.
Para pesquisar entre elementos:
indexOf/lastIndexOf(item, pos)
– procure item
começando na posição pos
e retorne o índice ou -1
se não for encontrado.
includes(value)
– retorna true
se o array tiver value
, caso contrário, false
.
find/filter(func)
– filtra os elementos através da função, retorna primeiro/todos os valores que a fazem retornar true
.
findIndex
é como find
, mas retorna o índice em vez de um valor.
Para iterar sobre elementos:
forEach(func)
– chama func
para cada elemento, não retorna nada.
Para transformar a matriz:
map(func)
– cria um novo array a partir dos resultados da chamada func
para cada elemento.
sort(func)
– classifica o array no local e depois o retorna.
reverse()
– inverte o array no local e depois o retorna.
split/join
– converte uma string em array e vice-versa.
reduce/reduceRight(func, initial)
– calcule um único valor no array chamando func
para cada elemento e passando um resultado intermediário entre as chamadas.
Adicionalmente:
Array.isArray(value)
verifica se value
é um array; se for, retorna true
, caso contrário, false
.
Observe que os métodos sort
, reverse
e splice
modificam o próprio array.
Esses métodos são os mais utilizados, cobrem 99% dos casos de uso. Mas existem alguns outros:
arr.some(fn)/arr.every(fn) verifique o array.
A função fn
é chamada em cada elemento do array semelhante a map
. Se algum/todos os resultados forem true
, retornará true
, caso contrário, false
.
Esses métodos se comportam como ||
e operadores &&
: se fn
retornar um valor verdadeiro, arr.some()
retornará imediatamente true
e interromperá a iteração sobre o restante dos itens; se fn
retornar um valor falso, arr.every()
retornará imediatamente false
e também interromperá a iteração sobre o restante dos itens.
Podemos usar every
para comparar arrays:
function matrizesEqual(arr1, arr2) { return arr1.length === arr2.length && arr1.every((valor, índice) => valor === arr2[índice]); } alert(arrayEqual([1, 2], [1, 2])); // verdadeiro
arr.fill(value, start, end) – preenche o array com value
repetidos do start
ao end
do índice.
arr.copyWithin(target, start, end) – copia seus elementos da posição start
até a posição end
para si mesmo , na posição target
(substitui os existentes).
arr.flat(profundidade)/arr.flatMap(fn) cria um novo array plano a partir de um array multidimensional.
Para a lista completa, consulte o manual.
À primeira vista, pode parecer que existem tantos métodos, bastante difíceis de lembrar. Mas, na verdade, isso é muito mais fácil.
Dê uma olhada na folha de dicas apenas para ficar ciente deles. Em seguida, resolva as tarefas deste capítulo para praticar, para que você tenha experiência com métodos de array.
Depois, sempre que você precisar fazer algo com um array e não souber como – venha aqui, dê uma olhada na folha de dicas e encontre o método certo. Os exemplos ajudarão você a escrevê-lo corretamente. Em breve você se lembrará automaticamente dos métodos, sem esforços específicos de sua parte.
importância: 5
Escreva a função camelize(str)
que transforma palavras separadas por traços como “my-short-string” em “myShortString” com caixa de camelo.
Ou seja: remove todos os travessões, cada palavra após o travessão fica em maiúscula.
Exemplos:
camelize("cor de fundo") == 'cordefundo'; camelize("imagem-estilo-lista") == 'listStyleImage'; camelize("-webkit-transition") == 'WebkitTransition';
PS Dica: use split
para dividir a string em um array, transforme-a e join
novamente.
Abra uma sandbox com testes.
função camelize(str) { retornar string .split('-') // divide 'my-long-word' em array ['my', 'long', 'word'] .mapa( // coloca em maiúscula as primeiras letras de todos os itens do array, exceto o primeiro // converte ['meu', 'longo', 'palavra'] em ['meu', 'longo', 'palavra'] (palavra, índice) => índice == 0 ? palavra: palavra[0].toUpperCase() + palavra.slice(1) ) .juntar(''); // junta ['my', 'Long', 'Word'] em 'myLongWord' }
Abra a solução com testes em uma sandbox.
importância: 4
Escreva uma função filterRange(arr, a, b)
que obtém um array arr
, procura elementos com valores maiores ou iguais a a
e menores ou iguais a b
e retorna um resultado como um array.
A função não deve modificar o array. Deve retornar o novo array.
Por exemplo:
deixe arr = [5, 3, 8, 1]; deixe filtrado = filterRange(arr, 1, 4); alerta(filtrado); // 3,1 (valores correspondentes) alerta(arr); // 5,3,8,1 (não modificado)
Abra uma sandbox com testes.
function filterRange(arr, a, b) { //adicionou colchetes ao redor da expressão para melhor legibilidade return arr.filter(item => (a <= item && item <= b)); } deixe arr = [5, 3, 8, 1]; deixe filtrado = filterRange(arr, 1, 4); alerta(filtrado); // 3,1 (valores correspondentes) alerta(arr); // 5,3,8,1 (não modificado)
Abra a solução com testes em uma sandbox.
importância: 4
Escreva uma função filterRangeInPlace(arr, a, b)
que obtém um array arr
e remove dele todos os valores, exceto aqueles que estão entre a
e b
. O teste é: a ≤ arr[i] ≤ b
.
A função deve modificar apenas o array. Não deve retornar nada.
Por exemplo:
deixe arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // removeu os números exceto de 1 a 4 alerta(arr); // [3, 1]
Abra uma sandbox com testes.
função filterRangeInPlace(arr, a, b) { for (seja i = 0; i < arr.length; i++) { deixe val = arr[i]; //remove se estiver fora do intervalo if (val < a || val > b) { arr.splice(i, 1); eu--; } } } deixe arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // removeu os números exceto de 1 a 4 alerta(arr); // [3, 1]
Abra a solução com testes em uma sandbox.
importância: 4
deixe arr = [5, 2, 1, -10, 8]; // ... seu código para classificá-lo em ordem decrescente alerta(arr); //8, 5, 2, 1, -10
deixe arr = [5, 2, 1, -10, 8]; arr.sort((a, b) => b - a); alerta(arr);
importância: 5
Temos uma matriz de strings arr
. Gostaríamos de ter uma cópia ordenada dele, mas mantenha arr
inalterado.
Crie uma função copySorted(arr)
que retorne tal cópia.
deixe arr = ["HTML", "JavaScript", "CSS"]; deixe classificado = copySorted(arr); alerta(classificado); // CSS, HTML, JavaScript alerta(arr); // HTML, JavaScript, CSS (sem alterações)
Podemos usar slice()
para fazer uma cópia e executar a classificação nela:
função copiarOrdenado(arr) { retornar arr.slice().sort(); } deixe arr = ["HTML", "JavaScript", "CSS"]; deixe classificado = copySorted(arr); alerta(classificado); alerta(arr);
importância: 5
Crie uma função construtora Calculator
que crie objetos de calculadora “extensíveis”.
A tarefa consiste em duas partes.
Primeiro, implemente o método calculate(str)
que pega uma string como "1 + 2"
no formato “NUMBER operador NUMBER” (delimitado por espaço) e retorna o resultado. Deve entender mais +
e menos -
.
Exemplo de uso:
deixe calc = nova Calculadora; alerta(calc.calculate("3 + 7") ); //10
Em seguida, adicione o método addMethod(name, func)
que ensina à calculadora uma nova operação. Leva o name
do operador e a função de dois argumentos func(a,b)
que o implementa.
Por exemplo, vamos adicionar a multiplicação *
, divisão /
e potência **
:
deixe powerCalc = nova Calculadora; powerCalc.addMethod("*", (a, b) => a * b); powerCalc.addMethod("/", (a, b) => a/b); powerCalc.addMethod("**", (a, b) => a ** b); deixe resultado = powerCalc.calculate("2 ** 3"); alerta(resultado); // 8
Não há parênteses ou expressões complexas nesta tarefa.
Os números e o operador são delimitados com exatamente um espaço.
Pode haver tratamento de erros se você quiser adicioná-lo.
Abra uma sandbox com testes.
Observe como os métodos são armazenados. Eles são simplesmente adicionados à propriedade this.methods
.
Todos os testes e conversões numéricas são feitos no método calculate
. No futuro, poderá ser estendido para suportar expressões mais complexas.
função Calculadora() { este.métodos = { "-": (a, b) => a - b, "+": (a, b) => a + b }; isto.calcular=função(str){ deixe dividir = str.split(' '), a = +divisão[0], op = divisão[1], b = +divisão[2]; if (!this.methods[op] || isNaN(a) || isNaN(b)) { retornar NaN; } retorne isto.métodos[op](a, b); }; this.addMethod = function(nome, func) { this.methods[nome] = func; }; }
Abra a solução com testes em uma sandbox.
importância: 5
Você tem uma matriz de objetos user
, cada um com user.name
. Escreva o código que o converte em um array de nomes.
Por exemplo:
deixe john = {nome: "John", idade: 25 }; deixe pete = {nome: "Pete", idade: 30 }; deixe mary = { nome: "Mary", idade: 28 }; deixe usuários = [joão, pete, mary]; deixe nomes = /* ... seu código */ alerta(nomes); // João, Pete, Maria
deixe john = {nome: "John", idade: 25 }; deixe pete = {nome: "Pete", idade: 30 }; deixe mary = { nome: "Mary", idade: 28 }; deixe usuários = [joão, pete, mary]; deixe nomes = usuários.map(item => item.nome); alerta(nomes); // João, Pete, Maria
importância: 5
Você tem uma matriz de objetos user
, cada um com name
, surname
e id
.
Escreva o código para criar outro array a partir dele, de objetos com id
e fullName
, onde fullName
é gerado a partir de name
e surname
.
Por exemplo:
deixe john = {nome: "John", sobrenome: "Smith", id: 1 }; deixe pete = {nome: "Pete", sobrenome: "Hunt", id: 2 }; deixe mary = {nome: "Mary", sobrenome: "Chave", id: 3 }; deixe usuários = [joão, pete, mary]; deixe usersMapped = /* ... seu código ... */ /* usuáriosMapped = [ {nome completo: "John Smith", id: 1}, {nome completo: "Pete Hunt", id: 2}, {nome completo: "Mary Key", id: 3} ] */ alerta(usersMapped[0].id) // 1 alerta(usersMapped[0].fullName) // John Smith
Então, na verdade você precisa mapear um array de objetos para outro. Tente usar =>
aqui. Há um pequeno problema.
deixe john = {nome: "John", sobrenome: "Smith", id: 1 }; deixe pete = {nome: "Pete", sobrenome: "Hunt", id: 2 }; deixe mary = {nome: "Mary", sobrenome: "Chave", id: 3 }; deixe usuários = [joão, pete, mary]; deixe usersMapped = users.map(user => ({ nome completo: `${user.name} ${user.surname}`, id: usuário.id })); /* usuáriosMapped = [ {nome completo: "John Smith", id: 1}, {nome completo: "Pete Hunt", id: 2}, {nome completo: "Mary Key", id: 3} ] */ alerta(userMapped[0].id); //1 alerta(userMapped[0].fullName); //João Smith
Observe que nas funções de seta precisamos usar colchetes adicionais.
Não podemos escrever assim:
deixe usersMapped = users.map(user => { nome completo: `${user.name} ${user.surname}`, id: usuário.id });
Como lembramos, existem duas funções de seta: sem body value => expr
e com body value => {...}
.
Aqui, o JavaScript trataria {
como o início do corpo da função, não o início do objeto. A solução alternativa é colocá-los entre colchetes “normais”:
deixe usersMapped = users.map(user => ({ nome completo: `${user.name} ${user.surname}`, id: usuário.id }));
Agora tudo bem.
importância: 5
Escreva a função sortByAge(users)
que obtém um array de objetos com a propriedade age
e os classifica por age
.
Por exemplo:
deixe john = {nome: "John", idade: 25 }; deixe pete = {nome: "Pete", idade: 30 }; deixe mary = { nome: "Mary", idade: 28 }; deixe arr = [pete, joão, mary]; sortByAge(arr); // agora: [joão, mary, pete] alerta(arr[0].nome); // John alerta(arr[1].nome); // Mary alerta(arr[2].nome); // Pete
function sortByAge(arr) { arr.sort((a, b) => a.idade - b.idade); } deixe john = {nome: "John", idade: 25 }; deixe pete = {nome: "Pete", idade: 30 }; deixe mary = { nome: "Mary", idade: 28 }; deixe arr = [pete, joão, mary]; sortByAge(arr); // agora classificado é: [john, mary, pete] alerta(arr[0].nome); // John alerta(arr[1].nome); // Mary alerta(arr[2].nome); // Pete
importância: 3
Escreva a função shuffle(array)
que embaralha (reordena aleatoriamente) os elementos do array.
Múltiplas execuções shuffle
podem levar a diferentes ordens de elementos. Por exemplo:
deixe arr = [1, 2, 3]; embaralhar(arr); // arr = [3, 2, 1] embaralhar(arr); // arr = [2, 1, 3] embaralhar(arr); // arr = [3, 1, 2] // ...
Todas as ordens de elementos devem ter probabilidade igual. Por exemplo, [1,2,3]
pode ser reordenado como [1,2,3]
ou [1,3,2]
ou [3,1,2]
etc, com igual probabilidade de cada caso.
A solução simples poderia ser:
função embaralhar(matriz) { array.sort(() => Math.random() - 0,5); } deixe arr = [1, 2, 3]; embaralhar(arr); alerta(arr);
Isso funciona um pouco, porque Math.random() - 0.5
é um número aleatório que pode ser positivo ou negativo; portanto, a função de classificação reordoma os elementos aleatoriamente.
Mas como a função de classificação não deve ser usada dessa maneira, nem todas as permutações têm a mesma probabilidade.
Por exemplo, considere o código abaixo. Ele executa shuffle
1000000 vezes e conta as aparências de todos os resultados possíveis:
função shuffle (matriz) { Array.sort (() => Math.random () - 0,5); } // Contagens de aparências para todas as permutações possíveis deixe count = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; para (vamos i = 0; i <1000000; i ++) { Deixe Array = [1, 2, 3]; shuffle (matriz); count [Array.join ('')] ++; } // mostra contagens de todas as permutações possíveis para (deixe a chave na contagem) { alert (`$ {key}: $ {count [key]}`); }
Um resultado de exemplo (depende do mecanismo JS):
123: 250706 132: 124425 213: 249618 231: 124880 312: 125148 321: 125223
Podemos ver claramente o viés: 123
e 213
aparecem com muito mais frequência do que outros.
O resultado do código pode variar entre os motores JavaScript, mas já podemos ver que a abordagem não é confiável.
Por que não funciona? De um modo geral, sort
é uma "caixa preta": lançamos uma matriz e uma função de comparação e esperamos que a matriz seja classificada. Mas, devido à total aleatoriedade da comparação, a caixa preta enlouquece e como exatamente fica louco depende da implementação concreta que difere entre os motores.
Existem outras boas maneiras de fazer a tarefa. Por exemplo, há um ótimo algoritmo chamado Fisher-Yates Shuffle. A idéia é andar na matriz na ordem inversa e trocar cada elemento com um aleatório antes:
função shuffle (matriz) { para (vamos i = array.length-1; i> 0; i--) { Seja j = math.floor (math.random () * (i + 1)); // índice aleatório de 0 a i // Swap Elements Array [i] e Array [J] // Usamos a sintaxe de "atribuição de destruição" para alcançar isso // Você encontrará mais detalhes sobre essa sintaxe nos capítulos posteriores // O mesmo pode ser escrito como: // deixe t = matriz [i]; Array [i] = Array [J]; Array [j] = t [Array [i], Array [J]] = [Array [J], Array [i]]; } }
Vamos testá -lo da mesma maneira:
função shuffle (matriz) { para (vamos i = array.length-1; i> 0; i--) { Seja j = math.floor (math.random () * (i + 1)); [Array [i], Array [J]] = [Array [J], Array [i]]; } } // Contagens de aparências para todas as permutações possíveis deixe count = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; para (vamos i = 0; i <1000000; i ++) { Deixe Array = [1, 2, 3]; shuffle (matriz); count [Array.join ('')] ++; } // mostra contagens de todas as permutações possíveis para (deixe a chave na contagem) { alert (`$ {key}: $ {count [key]}`); }
A saída de exemplo:
123: 166693 132: 166647 213: 166628 231: 167517 312: 166199 321: 166316
Parece bom agora: todas as permutações aparecem com a mesma probabilidade.
Além disso, o algoritmo Fisher-Yates é muito melhor, não há uma sobrecarga de "classificação".
Importância: 4
Escreva a função getAverageAge(users)
que recebe uma variedade de objetos com age
da propriedade e retorna a idade média.
A fórmula para a média é (age1 + age2 + ... + ageN) / N
.
Por exemplo:
Seja John = {Nome: "John", idade: 25}; Seja pete = {name: "pete", idade: 30}; Seja Mary = {Nome: "Mary", idade: 29}; Deixe arr = [John, Pete, Mary]; alerta (getAveraGeAge (ARR)); // (25 + 30 + 29) / 3 = 28
função getAveraGeAge (usuários) { Return users.reduce ((prev, user) => prev + user.age, 0) / users.length; } Seja John = {Nome: "John", idade: 25}; Seja pete = {name: "pete", idade: 30}; Seja Mary = {Nome: "Mary", idade: 29}; Deixe arr = [John, Pete, Mary]; alerta (getAveraGeAge (ARR)); // 28
Importância: 4
Deixe arr
ser uma matriz.
Crie uma função unique(arr)
que deve retornar uma matriz com itens exclusivos do arr
.
Por exemplo:
função exclusiva (arr) { / * Seu código */ } Deixe as cordas = ["Hare", "Krishna", "Hare", "Krishna", "Krishna", "Krishna", "Hare", "Hare", ": -o" ]; alerta (exclusivo (strings)); // Hare, Krishna,: -o
Abra uma caixa de areia com testes.
Vamos andar nos itens da matriz:
Para cada item, verificaremos se a matriz resultante já possui esse item.
Se for assim, ignore, caso contrário, adicione aos resultados.
função exclusiva (arr) { deixe o resultado = []; para (deixe str de arr) { if (! Result.includes (str)) { resultado.push (str); } } resultado de retorno; } Deixe as cordas = ["Hare", "Krishna", "Hare", "Krishna", "Krishna", "Krishna", "Hare", "Hare", ": -o" ]; alerta (exclusivo (strings)); // Hare, Krishna,: -o
O código funciona, mas há um problema de desempenho em potencial.
O Método result.includes(str)
caminha internamente o result
da matriz e compara cada elemento com str
para encontrar a partida.
Portanto, se houver 100
elementos no result
e ninguém corresponder str
, ele seguirá todo o result
e fará exatamente 100
comparações. E se result
for grande, como 10000
, haveria 10000
comparações.
Isso não é um problema por si só, porque os motores JavaScript são muito rápidos, portanto, o Walk 10000
Array é uma questão de microssegundos.
Mas fazemos esse teste para cada elemento do arr
, no loop for
.
Portanto, se arr.length
for 10000
teremos algo como 10000*10000
= 100 milhões de comparações. Isso é muito.
Portanto, a solução é boa apenas para pequenas matrizes.
Mais adiante no mapa do capítulo e definido, veremos como otimizá -lo.
Abra a solução com testes em uma caixa de areia.
Importância: 4
Digamos que recebemos uma variedade de usuários no formulário {id:..., name:..., age:... }
.
Crie um groupById(arr)
que cria um objeto a partir dele, com id
como a chave e a matriz itens como valores.
Por exemplo:
Deixe os usuários = [ {id: 'John', nome: "John Smith", idade: 20}, {id: 'Ann', nome: "Ann Smith", idade: 24}, {id: 'pete', nome: "Pete Peterson", idade: 31}, ]; deixe usuáriosbyId = groupById (usuários); /* // Após a ligação, devemos ter: usuáriosbyId = { John: {id: 'John', nome: "John Smith", idade: 20}, Ann: {id: 'Ann', nome: "Ann Smith", idade: 24}, Pete: {id: 'pete', nome: "Pete Peterson", idade: 31}, } */
Essa função é realmente útil ao trabalhar com dados do servidor.
Nesta tarefa, assumimos que id
é único. Pode não haver dois itens de matriz com o mesmo id
.
Por favor, use a matriz .reduce
Método na solução.
Abra uma caixa de areia com testes.
Grupo de funções (Array) { Return Array.Reduce ((obj, valor) => { obj [value.id] = value; retornar obj; }, {}) }
Abra a solução com testes em uma caixa de areia.