Copiar itens de matriz para outra matriz

918

Eu tenho uma matriz JavaScript dataArrayque eu quero enviar para uma nova matriz newArray. Exceto que eu não quero newArray[0]ser dataArray. Quero inserir todos os itens na nova matriz:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...

ou melhor ainda:

var newArray = new Array (
   dataArray1.values(),
   dataArray2.values(),
   // ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself
);

Portanto, agora a nova matriz contém todos os valores das matrizes de dados individuais. Existe alguma abreviação pushValuesdisponível para que eu não precise repetir cada indivíduo dataArray, adicionando os itens um por um?

bba
fonte
Esta deve ser a resposta davidwalsh.name/combining-js-arrays
starikovs
Isso responde sua pergunta? Como mesclar duas matrizes em JavaScript e duplicar itens
HackerMan 15/01

Respostas:

1249

Use a função concat , da seguinte maneira:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);

O valor de newArrayserá [1, 2, 3, 4]( arrayAe arrayBpermanecerá inalterado; concatcria e retorna uma nova matriz para o resultado).

WiseGuyEh
fonte
16
Concordo que a execução do desempenho é muito boa. MAS não é concat exatamente para esse fim concatenar com matrizes? Portanto, deve ser padrão . Ou há outras coisas melhores para fazer com concat? E pode ser lento apenas devido à má implementação do mecanismo JS do navegador ou onde quer que você o esteja usando? Pode ser corrigido um dia. Eu escolheria a manutenção do código em vez das otimizações de velocidade hacky. Hmm ...
Bitterblue
3
Também acabei de comparar a situação: concat vs. push.apply. Google Chrome: rápido (concat = vencedor) Opera,: rápido (concat = vencedor) ,: IEmais lento (concat = vencedor) Firefox,: lento (push.apply = vencedor, mas 10 vezes mais lento que o concat do Chrome) ... fale de má implementação do mecanismo JS .
Bitterblue
15
Como concatenar duas matrizes é a resposta aceita para colocar uma na outra ?! Essas são duas operações diferentes.
kaqqao
@kaqqao porque pushnão achatará uma matriz de valores. concatalcança o que a pergunta exige.
WiseGuyEh
644

Desde que suas matrizes não sejam enormes (veja a advertência abaixo), você pode usar o push()método da matriz à qual deseja acrescentar valores. push()pode usar vários parâmetros para que você possa usar seu apply()método para transmitir a matriz de valores a serem enviados como uma lista de parâmetros de função. Isso tem a vantagem concat()de adicionar elementos à matriz no lugar, em vez de criar uma nova matriz.

No entanto, parece que para matrizes grandes (da ordem de 100.000 membros ou mais), esse truque pode falhar . Para essas matrizes, usar um loop é uma abordagem melhor. Consulte https://stackoverflow.com/a/17368101/96100 para obter detalhes.

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);

Você pode generalizar isso em uma função:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}

... ou adicione-o ao Arrayprotótipo:

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);

... ou emule o push()método original permitindo vários parâmetros usando o fato de que concat(), como push(), permite vários parâmetros:

Array.prototype.pushArray = function() {
    this.push.apply(this, this.concat.apply([], arguments));
};

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);

Aqui está uma versão baseada em loop do último exemplo, adequada para matrizes grandes e todos os principais navegadores, incluindo o IE <= 8:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};
Tim Down
fonte
9
Nota: newArray.push.apply(newArray, dataArray1);dá o mesmo queArray.prototype.push.applay(newArray,dataArra1);
Isso é compatível com navegadores mais antigos?
Damien Ó Ceallaigh
1
@ DamienÓCeallaigh: Sim. Tudo isso é compatível com navegadores que retornam ao IE 6 e até mais cedo.
Tim Baixo
Este é o epítome das práticas de programação ruins. Array.prototype.concatnão é ruim, é simplesmente mal compreendido e às vezes mal utilizado. Nessas circunstâncias, é apenas mal compreendido, mas não mal utilizado.
precisa saber é o seguinte
446

Vou adicionar mais uma resposta "à prova de futuro"

No ECMAScript 6, você pode usar a sintaxe de propagação :

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

console.log(arr1)

A sintaxe de propagação ainda não está incluída nos principais navegadores. Para a compatibilidade atual, consulte esta tabela de compatibilidade (atualizada continuamente) .

No entanto, você pode usar a sintaxe de propagação com o Babel.js .

editar:

Veja a resposta de Jack Giffin abaixo para mais comentários sobre desempenho. Parece que concat ainda é melhor e mais rápido que o operador spread.

Karel Bílek
fonte
7
Você também pode usar o operador de propagação se estiver usando o TypeScript. Se você segmentar o ES5, ele será compilado para newArray.apply(newArray, dataArray1).
AJ Richardson
5
Nota: se você precisar do resultado em uma terceira matriz (portanto, não modificando arr1, como a pergunta inicial parecia exigir), você pode fazer newArray = [... arr1, ... arr2]
dim
Oh, eu não sabia disso. Obrigado. Eu adicionei um comentário de edição.
Karel Bílek
Semelhante ao modo como o concat funcionaria, com o bônus de ser persistente (alterando a matriz original).
Rob
@robertmylne O operador de propagação obviamente não modifica a matriz original, criando uma nova cópia de todo o conteúdo das matrizes que é separado.
Jack Giffin
145

Encontrou uma maneira elegante da MDN

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// Merge the second array into the first one
// Equivalent to vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

Ou você pode usar o spread operatorrecurso do ES6:

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]
Believe2014
fonte
1
Não é isso que a pergunta estava pedindo. A pergunta está pedindo uma maneira de criar uma nova matriz a cada vez, e não modificar uma matriz antiga.
Jack Giffin
13
var a=new Array('a','b','c');
var b=new Array('d','e','f');
var d=new Array('x','y','z');
var c=a.concat(b,d)

Isso resolve o seu problema?

Sébastien VINCENT
fonte
13

O seguinte me parece mais simples:

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

Como "push" recebe um número variável de argumentos, você pode usar o applymétodo da pushfunção para enviar todos os elementos de outra matriz. Ele constrói uma chamada para envio usando seu primeiro argumento ("newArray" aqui) como "this" e os elementos da matriz como os argumentos restantes.

A sliceprimeira instrução obtém uma cópia da primeira matriz, para que você não a modifique.

Atualizar Se você estiver usando uma versão do javascript com a fatia disponível, poderá simplificar a pushexpressão para:

newArray.push(...dataArray2)
shaunc
fonte
1
Também é mencionado aqui no MDN como um exemplo para "Mesclando duas matrizes"
Wilt
12

A função abaixo não apresenta problemas com o comprimento das matrizes e apresenta um desempenho melhor do que todas as soluções sugeridas:

function pushArray(list, other) {
    var len = other.length;
    var start = list.length;
    list.length = start + len;
    for (var i = 0; i < len; i++ , start++) {
        list[start] = other[i];
    }
}

infelizmente, o jspref se recusa a aceitar meus envios, então aqui estão os resultados usando o benchmark.js

        Name            |   ops/sec   |  ± %  | runs sampled
for loop and push       |      177506 |  0.92 | 63
Push Apply              |      234280 |  0.77 | 66
spread operator         |      259725 |  0.40 | 67
set length and for loop |      284223 |  0.41 | 66

Onde

para loop e push é:

    for (var i = 0, l = source.length; i < l; i++) {
        target.push(source[i]);
    }

Pressione Aplicar:

target.push.apply(target, source);

operador de spread:

    target.push(...source);

e finalmente o 'comprimento definido e loop for' é a função acima

Panos Theof
fonte
Esta pergunta está procurando uma maneira de criar uma nova matriz a cada vez, e não modificar uma matriz existente.
Jack Giffin
10

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀

Pelos fatos, um teste de desempenho no jsperf e a verificação de algumas coisas no console são realizados. Para a pesquisa, é utilizado o site irt.org . Abaixo está uma coleção de todas essas fontes reunidas, além de uma função de exemplo na parte inferior.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Método ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Matrizes esparsas SIM! ║Só o fatiado ║ não ║ Talvez 2   ║ não ║
║ mantido esparso ║ ║ matriz (1º arg) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Suporte ║ MSIE 4 ║ MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║ Borda 12 ║Source 
( fonte ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Atores de raio ║ não ║Só o empurrado ║ SIM! ! SIM! HaveSe tiver ║
Como uma matriz, matriz (2º argumento) eiterador 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Se o objeto do tipo matriz não tiver um propriedade Symbol.iterator , tente
  espalhá-lo lançará uma exceção.
2 Depende do código. O código de exemplo a seguir "YES" preserva a escassez.
function mergeCopyTogether(inputOne, inputTwo){
   var oneLen = inputOne.length, twoLen = inputTwo.length;
   var newArr = [], newLen = newArr.length = oneLen + twoLen;
   for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var two=0; i !== newLen; ++i, ++two) {
        tmp = inputTwo[two];
        if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
    }
    return newArr;
}

Como visto acima, eu argumentaria que o Concat é quase sempre o caminho a seguir tanto para o desempenho quanto para a capacidade de manter a escassez de matrizes sobressalentes. Em seguida, para curtidas de matriz (como DOMNodeLists comodocument.body.children ), eu recomendaria usar o loop for porque é o segundo melhor desempenho e o único outro método que retém matrizes esparsas. Abaixo, abordaremos rapidamente o que se entende por matrizes esparsas e curtidas de matrizes para esclarecer a confusão.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

No começo, algumas pessoas podem pensar que isso é um acaso e que os fornecedores de navegadores acabarão otimizando o Array.prototype.push para serem rápidos o suficiente para vencer o Array.prototype.concat. ERRADO! Array.prototype.concat sempre será mais rápido (pelo menos em princípio), porque é uma cópia-e-colar simples sobre os dados. Abaixo está um diagrama persuado-visual simplificado de como pode ser uma implementação de matriz de 32 bits (observe que implementações reais são MUITO mais complicadas)

Byte ║ Dados aqui
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 id ibid
0x02 ║ ibid
0x03 id ibid
0x00 ║ comprimento int = 0x00000001
0x01 id ibid
0x02 ║ ibid
0x03 id ibid
0x00 value int valueIndex = 0x00000000
0x01 id ibid
0x02 ║ ibid
0x03 id ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 id ibid
0x02 ║ ibid
0x03 id ibid
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (ou onde quer que esteja na memória)
0x01 id ibid
0x02 ║ ibid
0x03 id ibid

Como visto acima, tudo o que você precisa fazer para copiar algo assim é quase tão simples quanto copiá-lo byte por byte. Com Array.prototype.push.apply, é muito mais do que uma simples copiar e colar sobre os dados. O ".apply" deve verificar cada índice na matriz e convertê-lo em um conjunto de argumentos antes de passá-lo para Array.prototype.push. Em seguida, o Array.prototype.push precisa alocar adicionalmente mais memória a cada vez e (para algumas implementações de navegador) talvez até recalcular alguns dados de pesquisa de posição quanto à escassez.

Uma maneira alternativa de pensar nisso é essa. A matriz de origem 1 é uma grande pilha de papéis grampeados. A matriz de origem dois também é outra grande pilha de papéis. Seria mais rápido para você

  1. Vá para a loja, compre papel suficiente para obter uma cópia de cada matriz de origem. Em seguida, coloque as pilhas de papel de cada matriz de origem em uma copiadora e grampeie as duas cópias resultantes juntas.
  2. Vá para a loja, compre papel suficiente para uma única cópia da primeira matriz de origem. Em seguida, copie a matriz de origem para o novo papel manualmente, garantindo o preenchimento de quaisquer pontos esparsos em branco. Depois, volte para a loja, compre papel suficiente para a segunda matriz de origem. Em seguida, percorra a segunda matriz de origem e copie-a, garantindo que não haja espaços em branco na cópia. Depois, grampeie todos os papéis copiados.

Na analogia acima, a opção 1 representa Array.prototype.concat, enquanto a 2 representa Array.prototype.push.apply. Vamos testar isso com um JSperf semelhante, diferindo apenas no fato de este testar os métodos em matrizes esparsas, e não sólidas. Pode-se encontrar aqui .

Portanto, descubro que o futuro do desempenho desse caso de uso específico não está em Array.prototype.push, mas em Array.prototype.concat.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Quando certos membros da matriz estão simplesmente ausentes. Por exemplo:

// This is just as an example. In actual code, 
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] =  10;
mySparseArray[17] = "bar";
console.log("Length:   ", mySparseArray.length);
console.log("0 in it:  ", 0 in mySparseArray);
console.log("arr[0]:   ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10]   ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]:  ", mySparseArray[20]);

Como alternativa, o javascript permite inicializar matrizes sobressalentes facilmente.

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Um tipo de matriz é um objeto que possui pelo menos uma lengthpropriedade, mas não foi inicializado com new Arrayou []; Por exemplo, os objetos abaixo são classificados como de matriz.

{0: "foo", 1: "bar", comprimento: 2}
document.body.children
novo Uint8Array (3)
  • É semelhante a um array porque, embora seja um array (digitado), coagir para um array altera o construtor.
(function () {argumentos de retorno}) ()

Observe o que acontece usando um método que coage gostos de matrizes em matrizes como fatia.

  • NOTA: Não é recomendável chamar fatia nos argumentos da função devido ao desempenho.

Observe o que acontece usando um método que não coage gostos de matrizes em matrizes como concat.

Jack Giffin
fonte
9

Com o JavaScript ES6, você pode usar o operador ... como um operador de propagação que essencialmente converterá a matriz em valores. Então, você pode fazer algo assim:

const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];

const newArray = [
  ...myArray,
  ...moreData,
];

Embora a sintaxe seja concisa, não sei como isso funciona internamente e quais são as implicações de desempenho em grandes matrizes.

Ryan H.
fonte
2
Se você der uma olhada em como o babel o converte , verá que não deve ser mais lento do que usar a Array.push.applytécnica.
emil.c
1
@JackGiffin Acabei de me referir ao que Ryan mencionou que ele não sabe como funciona internamente e quais são as implicações de desempenho. Na verdade, eu não estava sugerindo essa abordagem. De qualquer forma, você fez um bom trabalho em sua resposta, boa pesquisa, é sempre bom conhecer esses detalhes.
emil.c
9

Aqui está a maneira ES6

var newArray = [];
let dataArray1 = [1,2,3,4]
let dataArray2 = [5,6,7,8]
newArray = [...dataArray1, ...dataArray2]
console.log(newArray)

O método acima é bom para a maioria dos casos, e nos casos em que não é considerado concat, como se você tivesse centenas de milhares de itens em matrizes.

    let dataArray1 = [1,2,3,4]
    let dataArray2 = [5,6,7,8]
    let newArray = dataArray1.concat(dataArray2);
    console.log(newArray)

Mamba negra
fonte
8

Há várias respostas falando sobre Array.prototype.push.apply . Aqui está um exemplo claro:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]
Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Se você possui a sintaxe do ES6:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
newArray.push(...dataArray1); // newArray = [1, 2]
newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Stephen Quan
fonte
4

Se você deseja modificar a matriz original, pode espalhar e pressionar:

var source = [1, 2, 3];
var range = [5, 6, 7];
var length = source.push(...range);

console.log(source); // [ 1, 2, 3, 5, 6, 7 ]
console.log(length); // 6

Se você deseja garantir que apenas itens do mesmo tipo entrem na sourcematriz (sem misturar números e seqüências de caracteres, por exemplo), use o TypeScript.

/**
 * Adds the items of the specified range array to the end of the source array.
 * Use this function to make sure only items of the same type go in the source array.
 */
function addRange<T>(source: T[], range: T[]) {
    source.push(...range);
}
orad
fonte
2

Temos duas matrizes a e b. o código que fez aqui é matriz, um valor é inserido na matriz b.

let a = [2, 4, 6, 8, 9, 15]

function transform(a) {
    let b = ['4', '16', '64']
    a.forEach(function(e) {
        b.push(e.toString());
    });
    return b;
}

transform(a)

[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]
KARTHIKEYAN.A
fonte
1
Não basta postar o código como resposta. Explique o que o código faz e como ele resolve o problema.
Patrick Hund
0

Tente o seguinte:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)
Trilok Singh
fonte
-2

em vez da função push (), use a função concat para o IE. exemplo,

var a=a.concat(a,new Array('amin'));
user1911703
fonte
1
ambos são muito IE compatível
Jack Giffin
-3

Esse é um código funcional e funciona bem:

var els = document.getElementsByTagName('input'), i;
var invnum = new Array();
var k = els.length;
for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}
user4354031
fonte