Os objetos permitem armazenar coleções de valores codificados. Isso é bom.
Mas muitas vezes descobrimos que precisamos de uma coleção ordenada , onde temos um primeiro, um segundo, um terceiro elemento e assim por diante. Por exemplo, precisamos disso para armazenar uma lista de algo: usuários, produtos, elementos HTML, etc.
Não é conveniente usar um objeto aqui, porque ele não fornece métodos para gerenciar a ordem dos elementos. Não podemos inserir uma nova propriedade “entre” as existentes. Os objetos simplesmente não foram feitos para esse uso.
Existe uma estrutura de dados especial chamada Array
, para armazenar coleções ordenadas.
Existem duas sintaxes para criar um array vazio:
deixe arr = new Array(); deixe arr = [];
Quase sempre, a segunda sintaxe é usada. Podemos fornecer elementos iniciais entre colchetes:
deixe frutas = ["Maçã", "Laranja", "Ameixa"];
Os elementos da matriz são numerados, começando com zero.
Podemos obter um elemento pelo seu número entre colchetes:
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; alerta(frutas[0]); // Maçã alerta(frutas[1]); // Laranja alerta(frutas[2]); // Ameixa
Podemos substituir um elemento:
frutas[2] = 'Pêra'; // agora ["Maçã", "Laranja", "Pêra"]
…Ou adicione um novo ao array:
frutas[3] = 'Limão'; // agora ["Maçã", "Laranja", "Pêra", "Limão"]
A contagem total dos elementos na matriz é o seu length
:
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; alerta(frutas.comprimento); //3
Também podemos usar alert
para mostrar todo o array.
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; alerta(frutas); // Maçã, Laranja, Ameixa
Um array pode armazenar elementos de qualquer tipo.
Por exemplo:
// mistura de valores deixe arr = [ 'Apple', {nome: 'John' }, true, function() { alert('olá'); } ]; // pega o objeto no índice 1 e depois mostra seu nome alerta(arr[1].nome); // John // pega a função no índice 3 e executa ela arr[3](); // olá
Vírgula final
Um array, assim como um objeto, pode terminar com uma vírgula:
deixe frutas = [ "Maçã", "Laranja", "Ameixa", ];
O estilo “vírgula final” facilita a inserção/remoção de itens, porque todas as linhas ficam iguais.
Uma adição recente
Esta é uma adição recente ao idioma. Navegadores antigos podem precisar de polyfills.
Digamos que queremos o último elemento do array.
Algumas linguagens de programação permitem o uso de índices negativos para a mesma finalidade, fruits[-1]
.
Embora em JavaScript não funcione. O resultado será undefined
, pois o índice entre colchetes é tratado literalmente.
Podemos calcular explicitamente o índice do último elemento e então acessá-lo: fruits[fruits.length - 1]
.
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; alerta(frutas[frutas.comprimento-1]); // Ameixa
Um pouco complicado, não é? Precisamos escrever o nome da variável duas vezes.
Felizmente, há uma sintaxe mais curta: fruits.at(-1)
:
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; // igual a frutas[frutas.length-1] alerta(frutas.at(-1)); // Ameixa
Em outras palavras, arr.at(i)
:
é exatamente o mesmo que arr[i]
, se i >= 0
.
para valores negativos de i
, ele recua do final da matriz.
Uma fila é um dos usos mais comuns de um array. Na ciência da computação, isso significa uma coleção ordenada de elementos que suporta duas operações:
push
anexa um elemento ao final.
shift
obtém um elemento desde o início, avançando a fila, para que o 2º elemento se torne o 1º.
Matrizes suportam ambas as operações.
Na prática, precisamos disso com muita frequência. Por exemplo, uma fila de mensagens que precisam ser mostradas na tela.
Há outro caso de uso para arrays – a estrutura de dados chamada pilha.
Suporta duas operações:
push
adiciona um elemento ao final.
pop
pega um elemento do final.
Assim, novos elementos são adicionados ou retirados sempre do “fim”.
Uma pilha é geralmente ilustrada como um baralho de cartas: novas cartas são adicionadas ao topo ou retiradas do topo:
Para pilhas, o último item enviado é recebido primeiro, o que também é chamado de princípio LIFO (Last-In-First-Out). Para filas, temos o FIFO (First-In-First-Out).
Matrizes em JavaScript podem funcionar tanto como fila quanto como pilha. Eles permitem que você adicione/remova elementos, tanto no início quanto no final.
Na ciência da computação, a estrutura de dados que permite isso é chamada deque.
Métodos que funcionam com o final do array:
pop
Extrai o último elemento do array e o retorna:
deixe frutas = ["Maçã", "Laranja", "Pêra"]; alerta(frutas.pop()); // remove "Pear" e alerta-o alerta(frutas); // Maçã, Laranja
fruits.pop()
fruits.at(-1)
retornam o último elemento do array, fruits.pop()
também modifica o array removendo-o.
push
Anexe o elemento ao final do array:
deixe frutas = ["Maçã", "Laranja"]; frutas.push("Pêra"); alerta(frutas); // Maçã, Laranja, Pêra
A fruits.push(...)
é igual fruits[fruits.length] = ...
.
Métodos que funcionam com o início do array:
shift
Extrai o primeiro elemento do array e o retorna:
deixe frutas = ["Maçã", "Laranja", "Pêra"]; alerta(frutas.shift()); // remove a Apple e alerta-a alerta(frutas); // Laranja, Pêra
unshift
Adicione o elemento ao início do array:
deixe frutas = ["Laranja", "Pêra"]; frutas.unshift('Maçã'); alerta(frutas); // Maçã, Laranja, Pêra
Os métodos push
e unshift
podem adicionar vários elementos de uma vez:
deixe frutas = ["Maçã"]; frutas.push("Laranja", "Pêssego"); frutas.unshift("Abacaxi", "Limão"); // ["Abacaxi", "Limão", "Maçã", "Laranja", "Pêssego"] alerta(frutas);
Um array é um tipo especial de objeto. Os colchetes usados para acessar uma propriedade arr[0]
na verdade vêm da sintaxe do objeto. Isso é essencialmente o mesmo que obj[key]
, onde arr
é o objeto, enquanto os números são usados como chaves.
Eles estendem objetos fornecendo métodos especiais para trabalhar com coleções ordenadas de dados e também a propriedade length
. Mas no fundo ainda é um objeto.
Lembre-se de que existem apenas oito tipos de dados básicos em JavaScript (consulte o capítulo Tipos de dados para obter mais informações). Array é um objeto e, portanto, se comporta como um objeto.
Por exemplo, é copiado por referência:
deixe frutas = ["Banana"] deixe arr = frutas; // copia por referência (duas variáveis referenciam o mesmo array) alerta(arr === frutas); // verdadeiro arr.push("Pêra"); //modifica o array por referência alerta(frutas); // Banana, Pêra - 2 itens agora
…Mas o que torna os arrays realmente especiais é a sua representação interna. O mecanismo tenta armazenar seus elementos na área de memória contígua, um após o outro, conforme mostrado nas ilustrações deste capítulo, e há outras otimizações também, para fazer com que os arrays funcionem realmente rápido.
Mas todos eles quebram se pararmos de trabalhar com um array como se fosse uma “coleção ordenada” e começarmos a trabalhar com ele como se fosse um objeto normal.
Por exemplo, tecnicamente podemos fazer isso:
deixe frutas = []; //faz um array frutas[99999] = 5; //atribui uma propriedade com índice muito maior que seu comprimento frutas.idade = 25; // cria uma propriedade com um nome arbitrário
Isso é possível porque arrays são objetos em sua base. Podemos adicionar quaisquer propriedades a eles.
Mas o mecanismo verá que estamos trabalhando com o array como se fosse um objeto normal. As otimizações específicas de array não são adequadas para tais casos e serão desativadas, seus benefícios desaparecerão.
As maneiras de usar indevidamente um array:
Adicione uma propriedade não numérica como arr.test = 5
.
Faça furos, como: adicione arr[0]
e depois arr[1000]
(e nada entre eles).
Preencha o array na ordem inversa, como arr[1000]
, arr[999]
e assim por diante.
Pense nos arrays como estruturas especiais para trabalhar com os dados ordenados . Eles fornecem métodos especiais para isso. Arrays são cuidadosamente ajustados dentro de mecanismos JavaScript para funcionar com dados ordenados contíguos, use-os desta forma. E se você precisar de chaves arbitrárias, há grandes chances de você realmente precisar de um objeto regular {}
.
Os métodos push/pop
são executados rapidamente, enquanto shift/unshift
são lentos.
Por que é mais rápido trabalhar com o final de um array do que com seu início? Vamos ver o que acontece durante a execução:
frutas.shift(); // pega 1 elemento desde o início
Não basta pegar e remover o elemento com índice 0
. Outros elementos também precisam ser renumerados.
A operação shift
deve fazer três coisas:
Remova o elemento com o índice 0
.
Mova todos os elementos para a esquerda, renumere-os do índice 1
para 0
, de 2
para 1
e assim por diante.
Atualize a propriedade length
.
Quanto mais elementos na matriz, mais tempo para movê-los e mais operações na memória.
O mesmo acontece com unshift
: para adicionar um elemento ao início do array, precisamos primeiro mover os elementos existentes para a direita, aumentando seus índices.
E o que há com push/pop
? Eles não precisam mover nada. Para extrair um elemento do final, o método pop
limpa o índice e encurta length
.
As ações para a operação pop
:
frutas.pop(); // pega 1 elemento do final
O método pop
não precisa mover nada, pois outros elementos mantêm seus índices. É por isso que é extremamente rápido.
A mesma coisa com o método push
.
Uma das maneiras mais antigas de alternar itens de array é o loop for
sobre índices:
deixe arr = ["Maçã", "Laranja", "Pêra"]; for (seja i = 0; i < arr.length; i++) { alerta(arr[i]); }
Mas para arrays existe outra forma de loop, for..of
:
deixe frutas = ["Maçã", "Laranja", "Ameixa"]; // itera sobre os elementos do array para (deixe fruto de frutas) { alerta(fruta); }
O for..of
não dá acesso ao número do elemento atual, apenas ao seu valor, mas na maioria dos casos isso é suficiente. E é mais curto.
Tecnicamente, como arrays são objetos, também é possível usar for..in
:
deixe arr = ["Maçã", "Laranja", "Pêra"]; for (deixe digitar arr) { alerta(arr[chave]); // Maçã, Laranja, Pêra }
Mas isso é realmente uma má ideia. Existem problemas potenciais com isso:
O loop for..in
itera sobre todas as propriedades , não apenas as numéricas.
Existem os chamados objetos “tipo array” no navegador e em outros ambientes, que se parecem com arrays . Ou seja, eles possuem propriedades length
e índices, mas também podem ter outras propriedades e métodos não numéricos, dos quais geralmente não precisamos. O loop for..in
irá listá-los. Portanto, se precisarmos trabalhar com objetos do tipo array, essas propriedades “extras” podem se tornar um problema.
O loop for..in
é otimizado para objetos genéricos, não para matrizes e, portanto, é 10 a 100 vezes mais lento. Claro, ainda é muito rápido. A aceleração só pode importar em gargalos. Mas ainda assim devemos estar cientes da diferença.
Geralmente, não devemos usar for..in
para arrays.
A propriedade length
é atualizada automaticamente quando modificamos o array. Para ser mais preciso, na verdade não é a contagem de valores na matriz, mas o maior índice numérico mais um.
Por exemplo, um único elemento com um índice grande fornece um comprimento grande:
deixe frutas = []; frutas[123] = "Maçã"; alerta(frutas.comprimento); //124
Observe que geralmente não usamos arrays assim.
Outra coisa interessante sobre a propriedade length
é que ela pode ser escrita.
Se aumentarmos manualmente, nada de interessante acontece. Mas se diminuirmos, o array será truncado. O processo é irreversível, aqui está o exemplo:
seja arr = [1, 2, 3, 4, 5]; arr.comprimento = 2; // trunca para 2 elementos alerta(arr); // [1, 2] arr.comprimento = 5; //retorna o comprimento de volta alerta(arr[3]); // indefinido: os valores não retornam
Portanto, a maneira mais simples de limpar o array é: arr.length = 0;
.
Existe mais uma sintaxe para criar um array:
deixe arr = new Array("Apple", "Pêra", "etc");
Raramente é usado porque os colchetes []
são mais curtos. Além disso, há um recurso complicado nele.
Se new Array
for chamado com um único argumento que é um número, ele criará um array sem itens, mas com o comprimento fornecido .
Vamos ver como alguém pode dar um tiro no próprio pé:
deixe arr = novo Array(2); // criará um array de [2] ? alerta(arr[0]); // indefinido! sem elementos. alerta(arr.length); // comprimento 2
Para evitar tais surpresas, normalmente usamos colchetes, a menos que saibamos realmente o que estamos fazendo.
Matrizes podem ter itens que também são matrizes. Podemos usá-lo para matrizes multidimensionais, por exemplo, para armazenar matrizes:
deixe matriz = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; alerta(matriz[0][1] ); // 2, o segundo valor do primeiro array interno
Arrays têm sua própria implementação do método toString
que retorna uma lista de elementos separados por vírgula.
Por exemplo:
deixe arr = [1, 2, 3]; alerta(arr); //1,2,3 alerta(String(arr) === '1,2,3' ); // verdadeiro
Além disso, vamos tentar isso:
alerta([] + 1); // "1" alerta([1] + 1); // "11" alerta([1,2] + 1); // "1,21"
Os arrays não possuem Symbol.toPrimitive
, nem um valueOf
viável, eles implementam apenas a conversão toString
, então aqui []
se torna uma string vazia, [1]
se torna "1"
e [1,2]
se torna "1,2"
.
Quando o operador binário mais "+"
adiciona algo a uma string, ele também o converte em uma string, então a próxima etapa é semelhante a esta:
alerta("" + 1); // "1" alerta("1" + 1); // "11" alerta("1,2" + 1); // "1,21"
Matrizes em JavaScript, diferentemente de outras linguagens de programação, não devem ser comparadas com o operador ==
.
Este operador não tem tratamento especial para arrays, ele funciona com eles como com qualquer objeto.
Vamos relembrar as regras:
Dois objetos são iguais ==
somente se forem referências ao mesmo objeto.
Se um dos argumentos de ==
for um objeto e o outro for um primitivo, então o objeto é convertido em primitivo, conforme explicado no capítulo Conversão de objeto em primitivo.
…Com exceção de null
e undefined
que são iguais ==
entre si e nada mais.
A comparação estrita ===
é ainda mais simples, pois não converte tipos.
Portanto, se compararmos arrays com ==
, eles nunca serão iguais, a menos que comparemos duas variáveis que fazem referência exatamente ao mesmo array.
Por exemplo:
alerta([] == []); // falso alerta( [0] == [0] ); // falso
Essas matrizes são objetos tecnicamente diferentes. Então eles não são iguais. O operador ==
não faz comparação item por item.
A comparação com primitivos também pode dar resultados aparentemente estranhos:
alerta(0 == []); // verdadeiro alerta('0' == [] ); // falso
Aqui, em ambos os casos, comparamos um objeto primitivo com um objeto array. Portanto, o array []
é convertido em primitivo para fins de comparação e se torna uma string vazia ''
.
Em seguida, o processo de comparação continua com as primitivas, conforme descrito no capítulo Conversões de Tipo:
// depois que [] foi convertido para '' alerta(0 == ''); // verdadeiro, pois '' é convertido para o número 0 alerta('0' == ''); // false, sem conversão de tipo, strings diferentes
Então, como comparar arrays?
É simples: não use o operador ==
. Em vez disso, compare-os item por item em um loop ou usando métodos de iteração explicados no próximo capítulo.
Array é um tipo especial de objeto, adequado para armazenar e gerenciar itens de dados ordenados.
A declaração:
// colchetes (normal) deixe arr = [item1, item2...]; // novo Array (excepcionalmente raro) deixe arr = new Array(item1, item2...);
A chamada para new Array(number)
cria um array com o comprimento determinado, mas sem elementos.
A propriedade length
é o comprimento do array ou, para ser mais preciso, seu último índice numérico mais um. É ajustado automaticamente por métodos de array.
Se encurtarmos length
manualmente, o array será truncado.
Obtendo os elementos:
podemos obter o elemento pelo seu índice, como arr[0]
também podemos usar o método at(i)
que permite índices negativos. Para valores negativos de i
, ele recua do final da matriz. Se i >= 0
, funciona da mesma forma que arr[i]
.
Podemos usar um array como deque com as seguintes operações:
push(...items)
adiciona items
ao final.
pop()
remove o elemento do final e o retorna.
shift()
remove o elemento do início e o retorna.
unshift(...items)
adiciona items
ao início.
Para fazer um loop sobre os elementos do array:
for (let i=0; i<arr.length; i++)
– funciona mais rápido e é compatível com navegadores antigos.
for (let item of arr)
– a sintaxe moderna apenas para itens,
for (let i in arr)
– nunca use.
Para comparar arrays, não use o operador ==
(assim como >
, <
e outros), pois eles não têm tratamento especial para arrays. Eles os tratam como quaisquer objetos, e não é isso que normalmente queremos.
Em vez disso, você pode usar o loop for..of
para comparar matrizes item por item.
Continuaremos com arrays e estudaremos mais métodos para adicionar, remover, extrair elementos e classificar arrays no próximo capítulo Métodos de array.
importância: 3
O que esse código vai mostrar?
deixe frutas = ["Maçãs", "Pêra", "Laranja"]; // coloca um novo valor na "cópia" deixe shoppingCart = frutas; shoppingCart.push("Banana"); // o que há nas frutas? alerta(frutas.comprimento); // ?
O resultado é 4
:
deixe frutas = ["Maçãs", "Pêra", "Laranja"]; deixe shoppingCart = frutas; shoppingCart.push("Banana"); alerta(frutas.comprimento); //4
Isso porque arrays são objetos. Portanto, shoppingCart
e fruits
são referências ao mesmo array.
importância: 5
Vamos tentar 5 operações de array.
Crie um array styles
com os itens “Jazz” e “Blues”.
Adicione “Rock-n-Roll” ao final.
Substitua o valor do meio por “Clássicos”. Seu código para encontrar o valor médio deve funcionar para qualquer array com comprimento ímpar.
Retire o primeiro valor do array e mostre-o.
Anexe Rap
e Reggae
ao array.
A matriz no processo:
jazz, blues Jazz, Blues, Rock n Roll Jazz, Clássicos, Rock n Roll Clássicos, Rock n Roll Rap, Reggae, Clássicos, Rock-n-Roll
deixe estilos = ["Jazz", "Blues"]; estilos.push("Rock n Roll"); estilos[Math.floor((styles.length - 1) / 2)] = "Clássicos"; alerta(estilos.shift()); estilos.unshift("Rap", "Reggae");
importância: 5
Qual é o resultado? Por que?
deixe arr = ["a", "b"]; arr.push(função() { alerta(este); }); arr[2](); // ?
A chamada arr[2]()
é sintaticamente o bom e velho obj[method]()
, no papel de obj
temos arr
, e no papel de method
temos 2
.
Portanto, temos uma chamada da função arr[2]
como método de objeto. Naturalmente, ele recebe this
referenciando o objeto arr
e gera o array:
deixe arr = ["a", "b"]; arr.push(função() { alerta(este); }) arr[2](); //a,b,função(){...}
O array possui 3 valores: inicialmente tinha dois, mais a função.
importância: 4
Escreva a função sumInput()
que:
Solicita valores ao usuário usando prompt
e armazena os valores no array.
Termina de perguntar quando o usuário insere um valor não numérico, uma string vazia ou pressiona “Cancelar”.
Calcula e retorna a soma dos itens do array.
PS Um zero 0
é um número válido, por favor não pare a entrada em zero.
Execute a demonstração
Observe os detalhes sutis, mas importantes da solução. Não convertemos value
em número instantaneamente após prompt
, porque depois de value = +value
não seríamos capazes de distinguir uma string vazia (sinal de parada) de zero (número válido). Em vez disso, fazemos isso mais tarde.
function somaInput() { deixe números = []; enquanto (verdadeiro) { deixe valor = prompt("Um número, por favor?", 0); // devemos cancelar? if (valor === "" || valor === nulo || !isFinite(valor)) break; números.push(+valor); } seja soma = 0; for (seja o número de números) { soma += número; } soma de retorno; } alerta(somaInput());
importância: 2
A entrada é uma matriz de números, por exemplo arr = [1, -2, 3, 4, -9, 6]
.
A tarefa é: encontrar o subarranjo contíguo de arr
com a soma máxima de itens.
Escreva a função getMaxSubSum(arr)
que retornará essa soma.
Por exemplo:
getMaxSubSum([-1, 2, 3, -9]) == 5 (a soma dos itens destacados) getMaxSubSum([2, -1, 2, 3, -9]) == 6 getMaxSubSum([-1, 2, 3, -9, 11]) == 11 getMaxSubSum([-2, -1, 1, 2]) == 3 getMaxSubSum([100, -9, 2, -3, 5]) == 100 getMaxSubSum([1, 2, 3]) == 6 (pegar tudo)
Se todos os itens forem negativos, significa que não pegamos nenhum (o subarray está vazio), então a soma é zero:
getMaxSubSum([-1, -2, -3]) = 0
Por favor, tente pensar em uma solução rápida: O(n 2 ) ou mesmo O(n), se puder.
Abra uma sandbox com testes.
Podemos calcular todas as subsomas possíveis.
A maneira mais simples é pegar cada elemento e calcular as somas de todos os subarrays a partir dele.
Por exemplo, para [-1, 2, 3, -9, 11]
:
// Começando em -1: -1 -1 + 2 -1 + 2 + 3 -1 + 2 + 3 + (-9) -1 + 2 + 3 + (-9) + 11 // A partir de 2: 2 2 + 3 2 + 3 + (-9) 2 + 3 + (-9) + 11 // A partir de 3: 3 3 + (-9) 3 + (-9) + 11 // A partir de -9 -9 -9 + 11 // A partir de 11 11
O código é na verdade um loop aninhado: o loop externo sobre os elementos da matriz e as contagens internas subsomas começando com o elemento atual.
função getMaxSubSum(arr) { deixe maxSoma = 0; // se não pegarmos nenhum elemento, zero será retornado for (seja i = 0; i < arr.length; i++) { deixe sumFixedStart = 0; for (seja j = i; j <arr.length; j++) { somaFixedStart += arr[j]; maxSum = Math.max(maxSum, sumFixedStart); } } retornar somamáx; } alerta(getMaxSubSum([-1, 2, 3, -9]) ); //5 alerta(getMaxSubSum([-1, 2, 3, -9, 11]) ); //11 alerta(getMaxSubSum([-2, -1, 1, 2]) ); //3 alerta(getMaxSubSum([1, 2, 3])); //6 alerta(getMaxSubSum([100, -9, 2, -3, 5])); // 100
A solução tem uma complexidade de tempo de O(n 2 ). Em outras palavras, se aumentarmos o tamanho do array 2 vezes, o algoritmo funcionará 4 vezes mais.
Para matrizes grandes (1.000, 10.000 ou mais itens), tais algoritmos podem levar a uma lentidão grave.
Vamos percorrer o array e manter a soma parcial atual dos elementos na variável s
. Se s
se tornar negativo em algum ponto, atribua s=0
. O máximo de todos esses s
será a resposta.
Se a descrição for muito vaga, consulte o código, é curto o suficiente:
função getMaxSubSum(arr) { deixe maxSoma = 0; deixe parcialSoma = 0; for (deixe item de arr) { // para cada item de arr soma parcial += item; //adiciona em parcialSum maxSum = Math.max(maxSum, parcialSoma); //lembra o máximo if (soma parcial < 0) soma parcial = 0; //zero se negativo } retornar somamáx; } alerta(getMaxSubSum([-1, 2, 3, -9]) ); //5 alerta(getMaxSubSum([-1, 2, 3, -9, 11]) ); //11 alerta(getMaxSubSum([-2, -1, 1, 2]) ); //3 alerta(getMaxSubSum([100, -9, 2, -3, 5])); // 100 alerta(getMaxSubSum([1, 2, 3])); //6 alerta(getMaxSubSum([-1, -2, -3]) ); //0
O algoritmo requer exatamente 1 passagem de array, então a complexidade de tempo é O(n).
Você pode encontrar informações mais detalhadas sobre o algoritmo aqui: Problema de subarranjo máximo. Se ainda não estiver óbvio por que isso funciona, trace o algoritmo nos exemplos acima e veja como funciona, isso é melhor do que qualquer palavra.
Abra a solução com testes em uma sandbox.