Fusionner/aplatir un tableau de tableaux en JavaScript?
j'ai un tableau JavaScript comme:
[[""], [""], [""], [""], [""], [""], [""]]
Comment pourrais-je fusionner les tableaux intérieurs séparés en un seul comme:
["", "", "", ...]
30 réponses
vous pouvez utiliser concat
pour fusionner des tableaux:
var arrays = [[""], [""], [""], [""], [""], [""], [""]];
var merged = [].concat.apply([], arrays);
utilisant la méthode apply
de concat
prendra juste le deuxième paramètre comme un tableau, donc la dernière ligne est identique à ceci:
var merged2 = [].concat([""], [""], [""], [""], [""], [""], [""]);
Voici une fonction courte qui utilise certaines des méthodes les plus récentes du tableau JavaScript pour aplatir un tableau n-dimensionnel.
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
Utilisation:
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]
Voici une solution fonctionnelle simple et performante:
var result = [].concat.apply([], [[1],[2,3],[4]]);
console.log(result); // [ 1, 2, 3, 4 ]
Aucun impératif de désordre.
il peut être mieux fait par JavaScript réduire la fonction.
var arrays = [[""], [""], [""], [""], [""], [""], [""], [""151900920""], [""],[""], [""], [""], ["0"], [""], [""], [""], [""]];
arrays = arrays.reduce(function(a, b){
return a.concat(b);
}, []);
ou, avec ES2015:
arrays = arrays.reduce((a, b) => a.concat(b), []);
la plupart des réponses ici ne fonctionnent pas sur des tableaux énormes (par exemple 200 000 éléments), et même s'ils le font, ils sont lents. polkovnikov.la réponse de ph a la meilleure performance, mais elle ne fonctionne pas pour un aplatissement profond.
Voici la solution la plus rapide, qui fonctionne aussi sur les tableaux avec plusieurs niveaux de nidification :
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;
};
exemples
tableaux de grande
flatten(Array(200000).fill([1]));
il gère d'énormes tableaux très bien. Sur ma machine ce code prend environ 14 ms à exécuter.
tableaux imbriqués
flatten(Array(2).fill(Array(2).fill(Array(2).fill([1]))));
il fonctionne avec des tableaux imbriqués. Ce code produit [1, 1, 1, 1, 1, 1, 1, 1]
.
tableaux avec différents niveaux de nidification
flatten([1, [1], [[1]]]);
il n'a aucun problème avec les tableaux aplatis comme celui-ci.
mise à jour: il s'est avéré que cette solution ne fonctionne pas avec les grands tableaux. Si vous cherchez une meilleure solution, plus rapide, Consultez cette réponse .
function flatten(arr) {
return [].concat(...arr)
}
est simplement étend arr
et passe comme arguments à concat()
, qui fusionne tous les tableaux en un seul. C'est l'équivalent de [].concat.apply([], arr)
.
vous pouvez aussi essayer ceci pour deep aplatissement:
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
)
)
}
Voir la démo sur JSBin .
Références ECMAScript 6 éléments utilisés dans cette réponse:
note latérale: les méthodes comme find()
et les fonctions de flèche ne sont pas supportées par tous les navigateurs, mais cela ne signifie pas que vous ne pouvez pas utiliser ces fonctionnalités dès maintenant. Il suffit d'utiliser Babel - il transforme le code ES6 en ES5.
vous pouvez utiliser Underscore :
var x = [[1], [2], [3, 4]];
_.flatten(x); // => [1, 2, 3, 4]
les procédures génériques signifient que nous n'avons pas à réécrire la complexité chaque fois que nous avons besoin d'utiliser un comportement spécifique.
concatMap
(ou flatMap
) est exactement ce dont nous avons besoin dans cette situation.
// 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 =
[[""], [""], [""], [""], [""], [""], [""]]
console.log (flatten (data))
prospective
et oui, vous l'avez deviné correctement, il ne plane un niveau, qui est exactement comment il devrait emploi 1519220920"
Imaginez un ensemble de données comme celui-ci
// Player :: (String, Number) -> Player
const Player = (name,number) =>
[ name, number ]
// team :: ( . Player) -> Team
const Team = (...players) =>
players
// Game :: (Team, Team) -> Game
const Game = (teamA, teamB) =>
[ teamA, teamB ]
// sample data
const teamA =
Team (Player ('bob', 5), Player ('alice', 6))
const teamB =
Team (Player ('ricky', 4), Player ('julian', 2))
const game =
Game (teamA, teamB)
console.log (game)
// [ [ [ 'bob', 5 ], [ 'alice', 6 ] ],
// [ [ 'ricky', 4 ], [ 'julian', 2 ] ] ]
Ok, maintenant dire que nous voulons imprimer un alignement qui montre tous les joueurs qui participeront à game
...
const gamePlayers = game =>
flatten (game)
gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]
si notre procédure flatten
aplatissait aussi les tableaux emboîtés, nous finirions avec ce résultat nul ...
const gamePlayers = game =>
badGenericFlatten(game)
gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]
rollin' de profondeur, bébé
cela ne veut pas dire que parfois vous ne voulez pas aplatir tableaux imbriqués, trop – seulement qui ne devrait pas être le comportement par défaut.
nous pouvons faire une "1519100920 procédure avec facilité ...
// 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)
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
concatMap (x =>
Array.isArray (x) ? deepFlatten (x) : x)
// your sample data
const data =
[0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]
console.log (flatten (data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]
console.log (deepFlatten (data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
là. Maintenant, vous avez un outil pour chaque tâche – un pour écraser un niveau d'imbrication, flatten
, et l'autre pour effacer toutes imbrication deepFlatten
.
peut-être vous pouvez l'appeler obliterate
ou nuke
si vous n'aimez pas le nom de deepFlatten
.
ne le répète pas deux fois !
bien sûr, les implémentations ci-dessus sont intelligentes et concises, mais l'utilisation d'un .map
suivi d'un appel à .reduce
signifie que nous faisons en fait plus d'itérations que nécessaire
à L'aide d'un combinateur fiable que j'appelle mapReduce
aide à garder les itérations à un minium; il prend une fonction de cartographie m :: a -> b
, une fonction de réduction r :: (b,a) ->b
et renvoie une nouvelle fonction de réduction - ce combinateur est au cœur de transducteurs ; si vous êtes intéressé, j'ai écrit d'autres réponses à leur sujet
// mapReduce = (a -> b, (b,a) -> b, (b,a) -> b)
const mapReduce = (m,r) =>
(acc,x) => r (acc, m (x))
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
xs.reduce (mapReduce (f, concat), [])
// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
xs.concat (ys)
// id :: a -> a
const id = x =>
x
// flatten :: [[a]] -> [a]
const flatten =
concatMap (id)
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
concatMap (x =>
Array.isArray (x) ? deepFlatten (x) : x)
// your sample data
const data =
[ [ [ 1, 2 ],
[ 3, 4 ] ],
[ [ 5, 6 ],
[ 7, 8 ] ] ]
console.log (flatten (data))
// [ [ 1. 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
console.log (deepFlatten (data))
// [ 1, 2, 3, 4, 5, 6, 7, 8 ]
une solution pour le cas plus général, lorsque vous pouvez avoir des éléments non-array dans votre array.
function flattenArrayOfArrays(a, r){
if(!r){ r = []}
for(var i=0; i<a.length; i++){
if(a[i].constructor == Array){
r.concat(flattenArrayOfArrays(a[i], r));
}else{
r.push(a[i]);
}
}
return r;
}
pour aplatir un tableau de tableaux à un seul élément, vous n'avez pas besoin d'importer une bibliothèque, une simple boucle est à la fois la plus simple et la plus efficace solution:
for (var i = 0; i < a.length; i++) {
a[i] = a[i][0];
}
À downvoters: veuillez lire la question, ne pas downvote, car il ne convient pas à votre problème différent. Cette solution est à la fois la plus rapide et la plus simple pour la question posée.
il y a une nouvelle méthode native ECMA 2018 appelée flat pour faire ceci exactement.
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]]
une autre solution ECMAScript 6 dans un style fonctionnel:
déclarer la fonction:
const flatten = arr => arr.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);
et l'utiliser:
flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6]
Qu'en est-il de l'utilisation de reduce(callback[, initialValue])
méthode de JavaScript 1.8
list.reduce( function( p,n){
return p.concat( n );
},[]);
s'il vous Plaît note: Quand Function.prototype.apply
( [].concat.apply([], arrays)
) ou de la propagation de l'opérateur ( [].concat(...arrays)
) est utilisé afin de l'aplatir un tableau, les deux peuvent causer des débordements de pile pour de grands tableaux, parce que chaque argument d'une fonction est stockée dans la pile.
Voici une implémentation sécurisée en mode fonctionnel qui met en balance les exigences les plus importantes les unes par rapport aux autres:
- réutilisabilité
- lisibilité
- concision
- performance
// 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));
dès que vous vous habituez aux petites fonctions de flèche dans la forme courbée, la composition de fonction et les fonctions d'ordre supérieur, ce code se lit comme prose. La programmation consiste alors simplement à assembler de petits blocs de construction qui fonctionnent toujours comme prévu, parce qu'ils ne contiennent pas d'effets secondaires.
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);
// gives ["a", "b", "c"]
(j'écris juste ceci comme une réponse séparée, basée sur le commentaire de @danhbear.)
ES6 Une Ligne Aplatir
Voir lodash aplatir , trait de soulignement aplatir (peu de true
)
function flatten(arr) {
return arr.reduce((acc, e) => acc.concat(e), []);
}
function flatten(arr) {
return [].concat.apply([], arr);
}
Testé avec
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 Une Ligne Profonde Aplatir
voir lodash flattenDeep , trait de soulignement aplatir
function flattenDeep(arr) {
return arr.reduce((acc, e) => Array.isArray(e) ? acc.concat(flattenDeep(e)) : acc.concat(e), []);
}
Testé avec
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]);
});
si vous avez seulement des tableaux avec un élément de chaîne de caractères:
[[""], [""], [""], [""]].join(',').split(',');
fera le travail. Bt qui correspond spécifiquement à votre exemple de code.
je l'ai fait à l'aide de la récursivité et de fermetures
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;
}
je préférerais transformer l'ensemble du tableau, tel quel, en chaîne, mais contrairement à d'autres réponses, ferait cela en utilisant JSON.stringify
et ne pas utiliser la méthode toString()
, qui produisent un résultat indésirable.
avec cette sortie JSON.stringify
, il ne reste plus qu'à enlever tous les crochets, envelopper le résultat avec les crochets de début et de fin encore une fois, et servir le résultat avec JSON.parse
qui ramène la chaîne à"vie".
- peut manipuler tableaux imbriqués infinis sans aucun coût de vitesse.
- peut gérer correctement les éléments de tableaux qui sont des chaînes contenant des virgules.
var arr = ["abc",[[[6]]],["3,4"],"2"];
var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);
console.log(flattened)
var arr = ["abc",[[[6]]],["3,4"],"2"];
var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);
console.log(flattened)
- Seulement pour multidimensionnelle Tableau de Chaînes de caractères et de Chiffres (pas d'Objets)
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))
ES5 flatten
fonction avec ES3 de secours pour la N-fois les tableaux imbriqués:
var flatten = (function() {
if (!!Array.prototype.reduce && !!Array.isArray) {
return function(array) {
return array.reduce(function(prev, next) {
return prev.concat(Array.isArray(next) ? flatten(next) : next);
}, []);
};
} else {
return function(array) {
var arr = [];
var i = 0;
var len = array.length;
var target;
for (; i < len; i++) {
target = array[i];
arr = arr.concat(
(Object.prototype.toString.call(target) === '[object Array]') ? flatten(target) : target
);
}
return arr;
};
}
}());
var a = [1, [2, [3, [4, [5]]]]];
console.log(flatten(a));
il semble que cela ressemble à un travail pour la récursion!
- gère plusieurs niveaux de nidification
- Poignées de tableaux vides et non les paramètres de tableau
- N'a pas de mutation
- ne s'appuie pas sur les fonctionnalités modernes du navigateur
Code:
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);
}
};
Utilisation:
flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7]
juste la meilleure solution sans lodash
let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
une approche 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);
ce n'est pas dur, il suffit d'itérer sur les tableaux et de les fusionner:
var result = [], input = [[""], [""], [""], [""], [""]];
for (var i = 0; i < input.length; ++i) {
result = result.concat(input[i]);
}
la logique ici est de convertir le tableau d'entrées en chaîne et supprimer tous les crochets([]) et analyser la sortie au tableau. J'utilise la fonctionnalité ES6 template pour cela.
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)
je plaisantais avec générateurs ES6 l'autre jour et a écrit ce gist . Qui contient...
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);
fondamentalement je crée un générateur qui boucle sur le tableau d'entrée original, s'il trouve un tableau il utilise l'opérateur rendement* en combinaison avec la récursion pour aplatir continuellement les tableaux internes. Si l'élément n'est pas un tableau, il suffit donne le seul élément. Puis en utilisant le ES6 Spread operator (alias splat operator) j'aplatit le Générateur dans une nouvelle instance de tableau.
Je n'ai pas testé la performance de ceci, mais je me dis que c'est un bel exemple simple de l'utilisation de générateurs et de l'opérateur de rendement.
mais encore une fois, je faisais l'idiot, donc je suis sûr qu'il y a d'autres façons plus performantes de faire ça.
je propose deux solutions courtes sans récursion. Ils ne sont pas optimaux d'un point de vue de la complexité computationnelle, mais fonctionnent bien dans les cas moyens:
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));
}
const flatten = array => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
par demande, ventiler la ligne a essentiellement ceci.
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
}
je recommande un fonction génératrice :
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
si désiré, créer un tableau de valeurs aplaties comme suit:
let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]