Come appiattire l’array annidato in javascript?

Come sappiamo, per appiattire l’array [[0, 1], [2, 3], [4, 5]] usando il metodo reduce()

 var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) { return a.concat(b); }); 

Quindi, come appiattire questo array [[[0], [1]], [[2], [3]], [[4], [5]]] a [0, 1, 2, 3, 4, 5] ?

Questa è un’alternativa alla ricorsione ( vedi jsfiddle qui ) e dovrebbe accettare qualsiasi livello di profondità che eviti l’overflow dello stack.

 var array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]]; console.log(flatten(array), array); // does not mutate array console.log(flatten(array, true), array); // array is now empty // This is done in a linear time O(n) without recursion // memory complexity is O(1) or O(n) if mutable param is set to false function flatten(array, mutable) { var toString = Object.prototype.toString; var arrayTypeStr = '[object Array]'; var result = []; var nodes = (mutable && array) || array.slice(); var node; if (!array.length) { return result; } node = nodes.pop(); do { if (toString.call(node) === arrayTypeStr) { nodes.push.apply(nodes, node); } else { result.push(node); } } while (nodes.length && (node = nodes.pop()) !== undefined); result.reverse(); // we reverse result to restore the original order return result; } 

Caso di utilizzo perfetto per la ricorsione, che potrebbe gestire una struttura ancora più profonda:

 function flatten(ary) { var ret = []; for(var i = 0; i < ary.length; i++) { if(Array.isArray(ary[i])) { ret = ret.concat(flatten(ary[i])); } else { ret.push(ary[i]); } } return ret; } flatten([[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]]) // [0, 1, 2, 3, 4, 5] 

In alternativa, come metodo Array:

 Array.prototype.flatten = function() { var ret = []; for(var i = 0; i < this.length; i++) { if(Array.isArray(this[i])) { ret = ret.concat(this[i].flatten()); } else { ret.push(this[i]); } } return ret; }; [[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flatten() // [0, 1, 2, 3, 4, 5] 

EDIT: Beh, pensa un modo un po 'funzionale (eccetto per la ricorsione chiamata che dovrebbe usare Y-combinator per puro funzionale: D).

 function flatten(ary) { return ary.reduce(function(a, b) { if (Array.isArray(b)) { return a.concat(flatten(b)) } return a.concat(b) }, []) } 

Adottiamo una syntax ES6 che la rende ancora più corta, in una sola riga.

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

Ma ricorda, questo non può essere applicato come un metodo di array, perché le funzioni di freccia non hanno il loro proprio this .

Stile ES6 con ricorsione:

 function flatten(arr) { const flat = [].concat(...arr); return flat.some(Array.isArray) ? flatten(flat) : flat; } 

Non è così efficiente, quindi non usare per qualcosa di più di casi relativamente banali, ma sembra abbastanza bello almeno.


Aggiornamento di giugno 2018:

Esiste ora una proposta ES per un metodo Array.prototype.flat . È attualmente allo stadio 3, il che significa che è probabile che verrà implementato dai browser presto (ish) e lo trasformsrà nelle specifiche nella sua forma attuale. Probabilmente ci sono alcuni polyfill in giro.

Esempio:

 const nested = [[[0], [1]], [[2], [3]], [[4], [5]]]; const flattened = nested.flat(2); // Need to specify depth if > 1 

Basato sulla soluzione di @ Leo, ma più veloce riutilizzando lo stesso array e prevenendo .concat

 function flatten(ary, ret) { ret = ret === undefined ? [] : ret; for (var i = 0; i < ary.length; i++) { if (Array.isArray(ary[i])) { flatten(ary[i], ret); } else { ret.push(ary[i]); } } return ret; } 

Esempio:

 function flatten(ary, ret) { ret = ret === undefined ? [] : ret; for (var i = 0; i < ary.length; i++) { if (Array.isArray(ary[i])) { flatten(ary[i], ret); } else { ret.push(ary[i]); } } return ret; } console.log(flatten([[[0], [1]], [[2], [3]], [[4], [5]]])); 

One-liner ES6:

 function flatten(a) { return Array.isArray(a) ? [].concat(...a.map(flatten)) : a; } 

Inoltre, versione non ricorsiva per array molto profondi (non molto efficienti ma piuttosto eleganti)

 function flatten(a) { var queue = a.slice(); var result = []; while(queue.length) { let curr = queue.pop(); if(Array.isArray(curr)) { queue.push(...curr); } else result.push(curr); } return result; } 

Solo 2 livelli:

 var arr = [1, [2, 3], [4, 5, 6]]; [].concat.apply([], arr) // -> [1, 2, 3, 4, 5, 6] 

Mi piace la mia soluzione 🙂

 var flattenClosure = function(a) { var store = []; return function() { var internMapper = function(b) { if (Array.isArray(b)) { return b.map(internMapper); } store.push(b); return b; } a.map(internMapper); return store; } }; console.log(flattenClosure([[[[[[[[1]]]], [2], [4], [6, 8, 9], 2]]], 10, 11, [15, 17, 20], [], 33])()); 

Ispirato al codice di Eloquent JavaScript e alla risposta fornita da @axelduch (Molto più efficiente da quello che posso dire anch’io).

 function flatten(array, mutable) { var nodes = (mutable && array) || array.slice(); // return a new array. var flattened = []; for (var node = nodes.shift(); node !== undefined; node = nodes.shift()) { if (Array.isArray(node)) { nodes.unshift.apply(nodes, node); } else { flattened.push(node); } } return flattened; } 

Disclaimer : So che si tratta di una domanda vecchia e già risposta, ma @Nick mi ha coinvolto perché ho commentato la sua risposta come uno dei modi più costosi per appiattire un array . Non ho codificato JavaScript per anni, ma è come andare in bicicletta – una volta imparato, non dimenticherai mai;)

Ecco il mio codice ricorsivo completo (non necessario for ciclo):

 var flattened = []; function flatten(a, i) { if(a.length > i) { if(Array.isArray(a[i])) flatten(a[i], 0); else flattened.push(a[i]); flatten(a, i + 1); } } flatten([[0, 1], [2, 3], [4, 5]], 0); console.log(flattened); 

L’ho provato contro la soluzione toString().split(',') e la mia è circa 7 volte più veloce. Questo è quello che intendo quando si parla di spese 😉

 function flatten(array) { return array.reduce( (previous, current) => Array.isArray(current) ? [...previous, ...flatten(current)] : [...previous, current] , [] ); } 

Se sei a conoscenza che l’array è composto solo da numeri, puoi semplicemente fare quanto segue:

 array.join().split(',').map(Number); 

Questo ha già una risposta, ma sto solo studiando JS e mi chiedo cosa ne pensi di:

  var array = [[[0], [1]], [[2], [3]], [[4], [5]]]; var flattend = array.join(",").split(","); console.log(flattend); 

L’unico effetto collaterale è che il join converte tutti gli elementi in stringhe, ma che può essere risolto con facilità

Ci sono tre funzioni di utilità in lodash relative alla tua domanda : flattenDeep , flattenDeep, flattenDepth in lodash . flatten va in una sola profondità, flattenDeep va fino in fondo al livello più profondo e flattenDepth ti dà la scelta di “quanto profondo” appiattire.

Esempio:

 > var arr = [[[0], [1]], [[2], [3]], [[4], [5]]]; > _.flattenDeep(arr) [0, 1, 2, 3, 4, 5] 

Un’implementazione con programmazione funzionale

Con la programmazione funzionale possiamo semplicemente derivare l’ flatten da un’altra funzione più generica: traverse .

Quest’ultimo è una funzione per attraversare e ridurre array arbitrariamente annidati, proprio come i flat array. Questo è ansible perché gli array annidati con profondità sconosciuta non sono più di una particolare versione di una struttura dati ad albero:

 const traverse = f => g => acc => xs => { let [leaf, stack] = xs[0][0] === undefined ? [xs[0], xs.slice(1)] : f([]) (xs); return stack.length ? traverse(f) (g) (g(leaf) (acc)) (stack) : g(leaf) (acc); }; const dfs = stack => tree => tree[0] === undefined ? [tree, stack] : dfs(tree.length > 1 ? concat(stack) (tree.slice(1)) : stack) (tree[0]); const concat = ys => xs => xs.concat(ys); const flatten = f => traverse(f) (concat) ([]); const xs = [[[1,2,3],4,5,6],7,8,[9,10,[11,12],[[13]],14],15]; console.log(flatten(dfs) (xs)); 
 var nested = [[[0], [1]], [[2], [3]], [[4], [5]]]; var flattened = [].concat.apply([],[].concat.apply([],nested)); console.log('-> flattened now: ' + flattened); 
 function flatten(x) { if (x.length == 0) {return []}; if (Array.isArray(x[0])) { return flatten(x[0].concat(flatten(x.slice(1,x.length)))); } return [].concat([x[0]], flatten(x.slice(1,x.length))); } 

appiattisce ricorsivamente l’array.

Basato sulla risposta di dashamble , ma credo che questo sia un po ‘più semplice da capire:

 var steamroller = function(arr) { var result = []; var dropHeavyObject = function(auxArr) { var flatnd = []; flatnd = auxArr.map(function(x) { if(Array.isArray(x)) { return dropHeavyObject(x); } else { result.push(x); return x; } }); return flatnd; }; dropHeavyObject(arr); return result; } 

Utilizzo di JSON.stringify e JSON.parse

 arr = JSON.parse("[" + JSON.stringify(arr) .replace(/[\[\]]+/g,"") .replace(/,,/g,",") + "]"); 
 // implementation function flattenArray(){ const input = arguments[0] , output = flatten(input) function flatten(){ return [].concat.apply([], arguments[0]) } return input.length === output.length ? output : flattenArray(output) } // how to use? flattenArray([1,2,[3]) // return [1,2,3] 

Test Case -> https://github.com/CHAOWEICHIU/ccw-custom-functions/blob/master/test/units/flattenArray.js

 function flatten(arrayOfArrays) { return arrayOfArrays.reduce(function(flat, subElem) { return flat.concat(Array.isArray(subElem) ? flatten(subElem) : subElem); }, []); } var arr0 = [0, 1, 2, 3, 4]; var arr1 = [[0,1], 2, [3, 4]]; var arr2 = [[[0, 1], 2], 3, 4]; console.log(flatten(arr0)); // [0, 1, 2, 3, 4] console.log(flatten(arr1)); // [0, 1, 2, 3, 4] console.log(flatten(arr2)); // [0, 1, 2, 3, 4] console.log(flatten([])); // [] 

Utilizzo delle convenzioni Lisp.

Tuttavia, usare .shift () e .concat () è inefficiente.

  flatten (array) { // get first element (car) and shift array (cdr) var car = array.shift(); // check to see if array was empty if (car === undefined) { return []; // if the first element (car) was an array, recurse on it } else if (_.isArray(car)) { return flatten(car).concat(flatten(array)); // otherwise, cons (concatenate) the car to the flattened version of cdr (rest of array) } else { return [car].concat(flatten(array)) } } 

Ho avuto questo problema l’altro giorno e ho scoperto un piccolo trucco … in realtà ho appena realizzato che è molto simile all’ultima risposta e come commento alla risposta sopra indica che non funzionerà per gli oggetti, ma per numeri semplici, ecc. funziona bene

Se si prende un array nidificato in stringa, separa i valori con virgole. Quindi puoi dividerlo con virgole per formare una stringa. Se è necessario convertire le stringhe in int o float, è ansible eseguire il nuovo array convertendo ciascun valore.

 stringArray = [[0, 1], [2, 3], [4, 5]].toString().split(','); stringArray.forEach((v,i,a) => a[i] = parseFloat(a[i])); 

Se hai una matrice infinita come quella qui sotto, ecco cosa farei.

 const a = [[1,2,[3]],4] Array.prototype.flatten = (array) => { const newArray = [] const flattenHelper = (array) => { array.map(i => { Array.isArray(i) ? flattenHelper(i) : newArray.push(i) }) } flattenHelper(a) return newArray } const newArray = a.flatten() console.log(newArray); 

Ecco cosa ho:

 function steamrollArray(arr) { // the flattened array var newArr = []; // recursive function function flatten(arr, newArr) { // go through array for (var i = 0; i < arr.length; i++) { // if element i of the current array is a non-array value push it if (Array.isArray(arr[i]) === false) { newArr.push(arr[i]); } // else the element is an array, so unwrap it else { flatten(arr[i], newArr); } } } flatten(arr, newArr); return newArr; } 

Può essere risolto in questo modo

 const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]]; const flatten(arr) => arr.reduce((acc, item) => acc.concat(Array.isArray(item) ? flatten(item) : item); }, []); console.log(flatten(array)); 

Essere consapevoli del fatto che in caso di array profondi, dovrebbe essere applicato il TCO

La versione con il TCO con la soluzione di ricorsione

 const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]]; const flatten = (() => { const _flatten = (acc, arr) => arr.reduce((acc, item) => acc.concat(Array.isArray(item) ? _flatten([], item) : item), acc); return arr => _flatten([], arr); })(); console.log(flatten(array)) 
 var flattenWithStack = function(arr) { var stack = []; var flat = []; stack.push(arr); while(stack.length > 0) { var curr = stack.pop(); if(curr instanceof Array) { stack = stack.concat(curr); } else { flat.push(curr); } } return flat.reverse(); } 

Non ricorsivo. Fondamentalmente dfs.

Pensa che questa funzione funzioni.

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