Mesclar / achatar uma matriz de matrizes em JavaScript?

Eu tenho uma matriz JavaScript como:

[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]] 

Como eu iria fundir as matrizes internas separadas em uma como:

 ["$6", "$12", "$25", ...] 

Você pode usar concat para mesclar matrizes:

 var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]; var merged = [].concat.apply([], arrays); 

O uso do método apply de concat apenas concat o segundo parâmetro como array, então a última linha é idêntica a esta:

 var merged2 = [].concat(["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]); 

Aqui está uma pequena function que usa alguns dos mais novos methods de matriz JavaScript para achatar uma matriz n-dimensional.

 function flatten(arr) { return arr.reduce(function (flat, toFlatten) { return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten); }, []); } 

Uso:

 flatten([[1, 2, 3], [4, 5]]); // [1, 2, 3, 4, 5] flatten([[[1, [1.1]], 2, 3], [4, 5]]); // [1, 1.1, 2, 3, 4, 5] 

Aqui está uma solução funcional simples e eficaz:

 var result = [].concat.apply([], [[1],[2,3],[4]]); console.log(result); // [ 1, 2, 3, 4 ] 

Pode ser melhor feito por javascript reduzir a function.

 var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]]; arrays = arrays.reduce(function(a, b){ return a.concat(b); }, []); 

Ou, com o ES2015:

 arrays = arrays.reduce((a, b) => a.concat(b), []); 

js-violino

Mozilla docs

A maioria das respostas aqui não funciona em matrizes enormes (por exemplo, 200.000 elementos), e mesmo se o fizerem, elas são lentas. A resposta de polkovnikov.ph tem o melhor desempenho, mas não funciona para um nivelamento profundo.

Aqui está a solução mais rápida, que também funciona em matrizes com vários níveis de aninhamento :

 const flatten = function(arr, result = []) { for (let i = 0, length = arr.length; i < length; i++) { const value = arr[i]; if (Array.isArray(value)) { flatten(value, result); } else { result.push(value); } } return result; }; 

Exemplos

Matrizes enormes

 flatten(Array(200000).fill([1])); 

Ele manipula grandes matrizes muito bem. Na minha máquina, esse código leva cerca de 14 ms para ser executado.

Matrizes aninhadas

 flatten(Array(2).fill(Array(2).fill(Array(2).fill([1])))); 

Trabalha com matrizes aninhadas. Este código produz [1, 1, 1, 1, 1, 1, 1, 1] .

Matrizes com diferentes níveis de aninhamento

 flatten([1, [1], [[1]]]); 

Não tem nenhum problema com arrays de achatamento como este.

Atualização: descobriu-se que esta solução não funciona com grandes matrizes. Você está procurando por uma solução melhor e mais rápida, confira esta resposta .


 function flatten(arr) { return [].concat(...arr) } 

É simplesmente expande arr e passa como argumentos para concat() , que mescla todos os arrays em um. É equivalente a [].concat.apply([], arr) .

Você também pode tentar isso para um nivelamento profundo:

 function deepFlatten(arr) { return flatten( // return shalowly flattened array arr.map(x=> // with each x in array Array.isArray(x) // is x an array? ? deepFlatten(x) // if yes, return deeply flattened x : x // if no, return just x ) ) } 

Veja demonstração no JSBin .

Referências para ECMAScript 6 elementos usados ​​nesta resposta:

  • Operador de spread
  • Funções de seta

Nota lateral: methods como find() e funções de seta não são suportados por todos os navegadores, mas isso não significa que você não pode usar esses resources agora. Basta usar o Babel – ele transforma o código ES6 em ES5.

Você pode usar o sublinhado :

 var x = [[1], [2], [3, 4]]; _.flatten(x); // => [1, 2, 3, 4] 

Procedimentos genéricos significam que não precisamos rewrite a complexidade toda vez que precisamos utilizar um comportamento específico.

concatMap (ou flatMap ) é exatamente o que precisamos nessa situação.

 // concat :: ([a],[a]) -> [a] const concat = (xs,ys) => xs.concat (ys) // concatMap :: (a -> [b]) -> [a] -> [b] const concatMap = f => xs => xs.map(f).reduce(concat, []) // id :: a -> a const id = x => x // flatten :: [[a]] -> [a] const flatten = concatMap (id) // your sample data const data = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]] console.log (flatten (data)) 

Uma solução para o caso mais geral, quando você pode ter alguns elementos que não são de matriz em sua matriz.

 function flattenArrayOfArrays(a, r){ if(!r){ r = []} for(var i=0; i 

Para nivelar uma matriz de arrays de elemento único, você não precisa importar uma biblioteca, um loop simples é a solução mais simples e mais eficiente :

 for (var i = 0; i < a.length; i++) { a[i] = a[i][0]; } 

Para downvoters: por favor, leia a pergunta, não faça downvote porque não combina com o seu problema muito diferente. Essa solução é a mais rápida e mais simples para a pergunta.

Outra solução ECMAScript 6 em estilo funcional:

Declare a function:

 const flatten = arr => arr.reduce( (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [] ); 

e usá-lo:

 flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6] 

Que tal usar reduce(callback[, initialValue]) método reduce(callback[, initialValue]) do JavaScript 1.8

 list.reduce( function( p,n){ return p.concat( n ); },[]); 

Observe: Quando Function.prototype.apply ( [].concat.apply([], arrays) ) ou o operador de dispersão ( [].concat(...arrays) ) é usado para achatar um array, ambos podem causar estouros de pilha para matrizes grandes, porque cada argumento de uma function é armazenado na pilha.

Aqui está uma implementação segura de pilha em estilo funcional que pesa os requisitos mais importantes uns contra os outros:

  • reutilização
  • legibilidade
  • concisão
  • desempenho
 // small, reusable auxiliary functions: const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // aka reduce const uncurry = f => (a, b) => f(a) (b); const concat = xs => y => xs.concat(y); // the actual function to flatten an array - a self-explanatory one-line: const flatten = xs => foldl(concat) ([]) (xs); // arbitrary array sizes (until the heap blows up :D) const xs = [[1,2,3],[4,5,6],[7,8,9]]; console.log(flatten(xs)); // Deriving a recursive solution for deeply nested arrays is trivially now // yet more small, reusable auxiliary functions: const map = f => xs => xs.map(apply(f)); const apply = f => a => f(a); const isArray = Array.isArray; // the derived recursive function: const flattenr = xs => flatten(map(x => isArray(x) ? flattenr(x) : x) (xs)); const ys = [1,[2,[3,[4,[5],6,],7],8],9]; console.log(flattenr(ys)); 

Há um novo método nativo ECMA 2018 chamado flat para fazer isso exatamente.

 const arr1 = [1, 2, [3, 4]]; arr1.flat(); // [1, 2, 3, 4] const arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] 
 var arrays = [["a"], ["b", "c"]]; Array.prototype.concat.apply([], arrays); // gives ["a", "b", "c"] 

(Eu só estou escrevendo isso como uma resposta separada, baseada no comentário de @danhbear.)

ES6 uma linha aplana

Veja lodash flatten , underscore flatten ( true superficial)

 function flatten(arr) { return arr.reduce((acc, e) => acc.concat(e), []); } 

ou

 function flatten(arr) { return [].concat.apply([], arr); } 

Testado com

 test('already flatted', () => { expect(flatten([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); }); test('flats first level', () => { expect(flatten([1, [2, [3, [4]], 5]])).toEqual([1, 2, [3, [4]], 5]); }); 

ES6 uma linha profunda aplainar

Veja Lodash flattenDeep , underscore flatten

 function flattenDeep(arr) { return arr.reduce((acc, e) => Array.isArray(e) ? acc.concat(flattenDeep(e)) : acc.concat(e), []); } 

Testado com

 test('already flatted', () => { expect(flattenDeep([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]); }); test('flats', () => { expect(flattenDeep([1, [2, [3, [4]], 5]])).toEqual([1, 2, 3, 4, 5]); }); 

Se você tiver apenas matrizes com um elemento de string:

 [["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(','); 

vai fazer o trabalho. Bt que corresponde especificamente ao seu exemplo de código.

Eu fiz isso usando recursion e encerramentos

 function flatten(arr) { var temp = []; function recursiveFlatten(arr) { for(var i = 0; i < arr.length; i++) { if(Array.isArray(arr[i])) { recursiveFlatten(arr[i]); } else { temp.push(arr[i]); } } } recursiveFlatten(arr); return temp; } 

Eu preferiria transformar toda a matriz, como está, em uma string, mas diferente de outras respostas, faria isso usando JSON.stringify e não usaria o toString() , que produz um resultado indesejado.

Com essa saída JSON.stringify , tudo o que resta é remover todos os colchetes, encapsular o resultado com os colchetes de início e de término novamente e servir o resultado com JSON.parse que traz a string de volta para “life”.

  • Pode manipular matrizes aninhadas infinitas sem nenhum custo de velocidade.
  • Pode corretamente manipular itens Array que são strings contendo vírgulas.

 var arr = ["abc",[[[6]]],["3,4"],"2"]; var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]"; var flattened = JSON.parse(s); console.log(flattened) 

 const common = arr.reduce((a, b) => [...a, ...b], []) 

Parece que isso parece um trabalho para a RECURSION!

  • Lida com vários níveis de aninhamento
  • Manipula arrays vazios e parâmetros não matriciais
  • Não tem mutação
  • Não depende de resources modernos do navegador

Código:

 var flatten = function(toFlatten) { var isArray = Object.prototype.toString.call(toFlatten) === '[object Array]'; if (isArray && toFlatten.length > 0) { var head = toFlatten[0]; var tail = toFlatten.slice(1); return flatten(head).concat(flatten(tail)); } else { return [].concat(toFlatten); } }; 

Uso:

 flatten([1,[2,3],4,[[5,6],7]]); // Result: [1, 2, 3, 4, 5, 6, 7] 

apenas a melhor solução sem lodash

 let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item)) 

Uma abordagem haskellesque

 function flatArray([x,...xs]){ return x ? [...Array.isArray(x) ? flatArray(x) : [x], ...flatArray(xs)] : []; } var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10]; fa = flatArray(na); console.log(fa); 

Maneira ES6:

 const flatten = arr => arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flatten(next) : next), []) const a = [1, [2, [3, [4, [5]]]]] console.log(flatten(a)) 

That’s not hard, just iterate over the arrays and merge them:

 var result = [], input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"]]; for (var i = 0; i < input.length; ++i) { result = result.concat(input[i]); } 

I was goofing with ES6 Generators the other day and wrote this gist . Which contains…

 function flatten(arrayOfArrays=[]){ function* flatgen() { for( let item of arrayOfArrays ) { if ( Array.isArray( item )) { yield* flatten(item) } else { yield item } } } return [...flatgen()]; } var flatArray = flatten([[1, [4]],[2],[3]]); console.log(flatArray); 

Basically I’m creating a generator that loops over the original input array, if it finds an array it uses the yield* operator in combination with recursion to continually flatten the internal arrays. If the item is not an array it just yields the single item. Then using the ES6 Spread operator (aka splat operator) I flatten out the generator into a new array instance.

I haven’t tested the performance of this, but I figure it is a nice simple example of using generators and the yield* operator.

But again, I was just goofing so I’m sure there are more performant ways to do this.

I propose two short solutions without recursion. They are not optimal from a computational complexity point of view, but work fine in average cases:

 let a = [1, [2, 3], [[4], 5, 6], 7, 8, [9, [[10]]]]; // Solution #1 while (a.find(x => Array.isArray(x))) a = a.reduce((x, y) => x.concat(y), []); // Solution #2 let i = a.findIndex(x => Array.isArray(x)); while (i > -1) { a.splice(i, 1, ...a[i]); i = a.findIndex(x => Array.isArray(x)); } 

The logic here is to convert input array to string and remove all brackets([]) and parse output to array. I’m using ES6 template feature for this.

 var x=[1, 2, [3, 4, [5, 6,[7], 9],12, [12, 14]]]; var y=JSON.parse(`[${JSON.stringify(x).replace(/\[|]/g,'')}]`); console.log(y) 
 const flatten = array => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []); 

Per request, Breaking down the one line is basically having this.

 function flatten(array) { // reduce traverses the array and we return the result return array.reduce(function(acc, b) { // if is an array we use recursion to perform the same operations over the array we found // else we just concat the element to the accumulator return acc.concat( Array.isArray(b) ? flatten(b) : b); }, []); // we initialize the accumulator on an empty array to collect all the elements } 

I recommend a space-efficient generator function :

 function* flatten(arr) { if (!Array.isArray(arr)) yield arr; else for (let el of arr) yield* flatten(el); } // Example: console.log(...flatten([1,[2,[3,[4]]]])); // 1 2 3 4