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:

["", "", "", ...]
753
demandé sur AndrewL 2012-06-02 22:53:46

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([""], [""], [""], [""], [""], [""], [""]);
1331
répondu Gumbo 2017-09-17 09:02:12

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]
368
répondu Noah Freitas 2015-07-02 17:40:35

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.

275
répondu Nikita Volkov 2016-10-20 21:17:24

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), []);

js-violon

Mozilla docs

147
répondu user2668376 2017-06-22 07:39:15

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.

55
répondu Michał Perłakowski 2018-03-05 10:12:47

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.

49
répondu Michał Perłakowski 2017-05-23 12:18:30

vous pouvez utiliser Underscore :

var x = [[1], [2], [3, 4]];

_.flatten(x); // => [1, 2, 3, 4]
42
répondu Todd Yandell 2012-06-02 18:58:38

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 ]
36
répondu user633183 2017-09-28 07:21:38

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;
}
28
répondu Trindaz 2017-03-28 01:28:19

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.

21
répondu Denys Séguret 2016-08-08 07:20:23

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]]
17
répondu Alister Norris 2018-08-30 12:23:32

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]
16
répondu diziaq 2016-02-01 10:49:11

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  );
},[]);
13
répondu rab 2013-04-30 11:50:33

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.

13
répondu ftor 2016-08-23 16:34:08
const common = arr.reduce((a, b) => [...a, ...b], [])
11
répondu YairTawil 2018-08-27 08:53:16
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.)

7
répondu VasyaNovikov 2015-02-16 07:32:53

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), []);
}

ou

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]);
});
7
répondu zurfyx 2018-03-22 22:00:19

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.

6
répondu Florian Salihovic 2012-06-02 19:18:08

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;
}
6
répondu balajiv 2015-04-29 12:10:50

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)

  • Seulement pour multidimensionnelle Tableau de Chaînes de caractères et de Chiffres (pas d'Objets)
6
répondu vsync 2016-11-02 09:02:14

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));
6
répondu Artem Gavrysh 2018-02-20 10:50:18

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] 
5
répondu Jai 2014-04-01 03:35:37

juste la meilleure solution sans lodash

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
5
répondu Vlad Ankudinov 2016-09-07 10:20:52

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);
5
répondu Redu 2017-06-01 15:49:26

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]);
}
4
répondu Niko 2012-06-02 18:56:43

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)
4
répondu R Santosh Reddy 2017-04-24 12:42:31

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.

3
répondu ashwell 2016-01-28 22:42:39

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));
}
3
répondu Tommaso Ognibene 2016-11-30 16:11:53
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
}
3
répondu alejandro 2017-05-16 02:07:12

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]
3
répondu le_m 2017-05-22 02:32:23