Pregunta Fusionar / aplanar una matriz de matrices en JavaScript?


Tengo una matriz de JavaScript como:

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

¿Cómo voy a fusionar los arreglos internos separados en uno como este?

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

727
2018-06-02 18:53


origen


Respuestas:


Puedes usar concat para combinar matrices:

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

Utilizando el apply método de concat simplemente tomará el segundo parámetro como una matriz, por lo que la última línea es idéntica a esto:

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

1289
2018-06-02 18:56



Aquí hay una breve función que utiliza algunos de los métodos de matriz de JavaScript más nuevos para aplanar una 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]

352
2018-02-22 17:41



Aquí hay una solución funcional simple y eficaz:

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

Sin lío imperativo.


270
2018-03-13 22:12



Se puede hacer mejor mediante la función javascript reduce.

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

O bien, con ES2015:

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

js-fiddle

Documentos de Mozilla


144
2017-08-19 05:58



La mayoría de las respuestas aquí no funcionan en arreglos enormes (por ejemplo, 200 000 elementos), e incluso si lo hacen, son lentos. La respuesta de polkovnikov.ph tiene el mejor rendimiento, pero no funciona para el aplanamiento profundo.

Aquí está la solución más rápida, que también funciona en arreglos con múltiples niveles de anidamiento:

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;
};

Ejemplos

Enormes arreglos

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

Maneja arreglos enormes muy bien. En mi máquina, este código tarda unos 14 ms en ejecutarse.

Matrices anidadas

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

Funciona con matrices anidadas. Este código produce [1, 1, 1, 1, 1, 1, 1, 1].

Matrices con diferentes niveles de anidamiento

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

No tiene ningún problema para acoplar matrices como esta.


50
2017-08-17 14:54



Actualización: resultó que esta solución no funciona con grandes matrices. Si está buscando una solución mejor y más rápida, consulte esta respuesta.


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

Simplemente se expande arr y lo pasa como argumentos a concat(), que combina todas las matrices en una. Es equivalente a [].concat.apply([], arr).

También puedes probar esto para un aplanamiento 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
    )
  )
}

Ver demostración en JSBin.

Referencias para los elementos de ECMAScript 6 utilizados en esta respuesta:


Nota al margen: métodos como find() y las funciones de flecha no son compatibles con todos los navegadores, pero eso no significa que no pueda usar estas funciones en este momento. Solo usa Babel - transforma el código ES6 en ES5.


47
2018-01-24 19:31



Puedes usar Guion bajo:

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

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

41
2018-06-02 18:58



Los procedimientos genéricos significan que no tenemos que volver a escribir la complejidad cada vez que necesitamos utilizar un comportamiento específico.

concatMap (o flatMap) es exactamente lo que necesitamos en esta situación.

// 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))

previsión

Y sí, lo adivinaste correctamente, solo se aplana uno nivel, que es exactamente cómo debería trabajo

Imagina un conjunto de datos como este

// 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, ahora digamos que queremos imprimir una lista que muestre a todos los jugadores que participarán en game ...

const gamePlayers = game =>
  flatten (game)

gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]

Si nuestro flatten procedimiento aplanó matrices anidadas también, terminamos con este resultado de basura ...

const gamePlayers = game =>
  badGenericFlatten(game)

gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]

rollin 'profundo, bebé

Eso no quiere decir que a veces tampoco quieres aplanar matrices anidadas; solo que eso no debería ser el comportamiento predeterminado.

Podemos hacer un deepFlatten procedimiento con facilidad ...

// 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 ]

Ahí. Ahora tiene una herramienta para cada trabajo, una para aplastar un nivel de anidación, flatteny uno para borrar todo anidación deepFlatten.

Quizás puedas llamarlo obliterate o nuke si no te gusta el nombre deepFlatten.


¡No iteres dos veces!

Por supuesto, las implementaciones anteriores son inteligentes y concisas, pero usando una .map seguido de una llamada a .reduce significa que estamos haciendo más iteraciones de lo necesario

Usando un combinador confiable estoy llamando mapReduce ayuda a mantener las iteraciones a un minuto; toma una función de mapeo m :: a -> b, una función reductora r :: (b,a) ->b y devuelve una nueva función de reducción: este combinador está en el corazón de transductores; si estás interesado, He escrito otras respuestas sobre ellos

// 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 ]


34
2017-08-18 19:34



Una solución para el caso más general, cuando puede tener algunos elementos que no sean de matriz en su matriz.

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
2018-06-06 04:41



Para aplanar una matriz de matrices de un solo elemento, no es necesario importar una biblioteca, un bucle simple es el más simple y el más simple. más eficiente solución:

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

Para los detractores: por favor, lea la pregunta, no disminuya la votación porque no se ajusta a su problema muy diferente. Esta solución es tanto la más rápida como la más simple para la pregunta planteada.


19
2018-06-02 18:56



Otra solución ECMAScript 6 en estilo funcional:

Declarar función:

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

y úsala:

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

17
2018-02-01 10:49