Объединить / сгладить массив массивов в JavaScript?


у меня есть массив JavaScript, например:

[[""], [""], [""], [""], [""], [""], [""]]

Как бы я пошел о слиянии отдельных внутренних массивов в один, как:

["", "", "", ...]
30 753

30 ответов:

можно использовать concat для объединения массивов:

var arrays = [[""], [""], [""], [""], [""], [""], [""]];
var merged = [].concat.apply([], arrays);

С помощью apply метод concat будет просто взять второй параметр в виде массива, так что последняя строка идентична этому:

var merged2 = [].concat([""], [""], [""], [""], [""], [""], [""]);

вот короткая функция, которая использует некоторые из новых методов массива JavaScript для сглаживания n-мерного массива.

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

использование:

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]

вот простое и эффективное функциональное решение:

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

Не важно беспорядок.

это лучше всего сделать с помощью функции JavaScript reduce.

var arrays = [[""], [""], [""], [""], [""], [""], [""], [""], [""],[""], [""], [""], ["0"], [""], [""], [""], [""]];

arrays = arrays.reduce(function(a, b){
     return a.concat(b);
}, []);

или, с ES2015:

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

js-fiddle

Mozilla docs

большинство ответов здесь не работают на огромных (например, 200 000 элементов) массивах, и даже если они это делают, они медленные. полковников.ответ ПШ имеет самое лучшее представление, но оно не работает для глубокий сплющивать.

здесь самое быстрое решение, которое работает также на массивах с несколькими уровнями вложенности:

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

примеры

огромные массивы

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

он обрабатывает огромные массивы просто отлично. На моей машине этот код занимает около 14 мс для выполнения.

вложенные массивы

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

Он работает с вложенными массивами. Этот код производит [1, 1, 1, 1, 1, 1, 1, 1].

массивы с разными уровнями вложенности

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

Он не имеет никаких проблем с выравниванием массивов, как этот.

Update: оказалось, что это решение не работает с большими массивами. Это вы ищете лучшее, более быстрое решение, проверьте ответ.


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

просто расширяет arr и передает ей в качестве аргументов concat(), который объединяет все массивы в один. Это эквивалентно [].concat.apply([], arr).

вы также можете попробовать это для глубокого выравнивания:

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

посмотреть демо на JSBin.

ссылки на элементы ECMAScript 6, используемые в этом ответе:


Примечание: методы как find() и стрелочные функции поддерживаются не всеми браузерами, но это не значит, что вы не можете использовать эти функции прямо сейчас. Просто используйте Бабель - он преобразует код ES6 в ES5.

можно использовать подчеркивание:

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

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

общие процедуры означают, что нам не нужно переписывать сложность каждый раз, когда нам нужно использовать определенное поведение.

concatMap (или flatMap) - это именно то, что нам нужно в этой ситуации.

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

предвидение

и да, вы угадали правильно, это только сглаживает один уровень, который именно так это должны работа

представьте себе некоторый набор данных, как это

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

хорошо, теперь скажем, что мы хотим напечатать список, который показывает всех игроков, которые будут участвовать в game...

const gamePlayers = game =>
  flatten (game)

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

если наши flatten процедура сплющенные вложенные массивы тоже, мы бы в конечном итоге с этим результатом мусора ...

const gamePlayers = game =>
  badGenericFlatten(game)

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

катится глубоко, детка

это не значит, что иногда вы не хотите сглаживать вложенные массивы-только это не должно быть по умолчанию поведение.

можно сделать deepFlatten процедура с легкостью ...

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

там. Теперь у вас есть инструмент для каждого задания – один для раздавливания одного уровня вложенности, flatten, и один для уничтожения всех вложений deepFlatten.

может быть, вы можете назвать это obliterate или nuke если вам не нравится имя deepFlatten.


не повторяйте дважды !

конечно выше реализации умны и лаконичны, но с помощью .map затем последовал звонок .reduce означает, что мы на самом деле делаем больше итераций, чем необходимо

используя надежный комбинатор я звоню mapReduce помогает сохранить итерации до минимума; он принимает функцию отображения m :: a -> b, восстанавливающее функции r :: (b,a) ->b и возвращает новую функцию сокращения-этот комбинатор находится в центре датчики; если вам интересно,я написал другие ответы о их

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

решение для более общего случая, когда в вашем массиве могут быть некоторые элементы, отличные от массива.

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

чтобы сгладить массив одноэлементных массивов, вам не нужно импортировать библиотеку, простой цикл является одновременно самым простым и наиболее эффективным!--3--> устранение :

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

для downvoters: пожалуйста, прочитайте вопрос, не понижайте голос, потому что он не подходит для вашей совсем другой проблемы. Это решение является одновременно самым быстрым и простым для заданного вопроса.

есть новый собственный метод ECMA 2018 под названием квартира чтобы сделать это точно.

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

другое решение ECMAScript 6 в функциональном стиле:

функции объявить:

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

и использовать его:

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

как насчет использования reduce(callback[, initialValue]) метод JavaScript 1.8

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

обратите внимание:, когда Function.prototype.apply ([].concat.apply([], arrays)) или распространение оператор ([].concat(...arrays)) используется для того, чтобы выровнять массив, оба могут вызвать переполнение стека для больших массивов, потому что каждый аргумент функции хранится в стеке.

вот стек-безопасный реализации в функциональном стиле, в которой оцениваются наиболее важные требования в отношении одного другой:

  • возможность повторного использования
  • читабельности
  • краткость
  • производительность

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

как только вы привыкнете к маленьким функциям стрелки в каррированной форме, композиции функций и функций более высокого порядка, этот код читается как проза. Программирование тогда просто состоит из объединения небольших строительных блоков, которые всегда работают так, как ожидалось, потому что они не содержат никакой стороны эффекты.

const common = arr.reduce((a, b) => [...a, ...b], [])
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

// gives ["a", "b", "c"]

(Я просто пишу это как отдельный ответ, основанный на комментарии @danhbear.)

ES6 одна линия сгладить

посмотреть выровнять лодашь,подчеркивание сгладить (мелкий true)

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

или

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

протестировано с

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 одна линия глубоко сплющить

посмотреть лодаш flattenDeep,подчеркнуть расплющить

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

протестировано с

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

Если у вас есть только массивы с 1 строка:

[[""], [""], [""], [""]].join(',').split(',');

сделает работу. Bt, который специально соответствует вашему примеру кода.

Я сделал это с помощью рекурсии и временное закрытие

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

Я бы предпочел преобразовать весь массив, как есть, в строку, но в отличие от других ответов, сделал бы это с помощью JSON.stringify и не использовать toString() метод, который дает нежелательный результат.

С JSON.stringify выход, все, что осталось, это удалить все скобки, обернуть результат с помощью скобок start & ending еще раз и подать результат с помощью JSON.parse который возвращает строку к "жизни".

  • может обрабатывать бесконечные вложенные массивы без какой-либо скорости затраты.
  • может правильно обрабатывать элементы массива, которые являются строки, содержащие запятые.

var arr = ["abc",[[[6]]],["3,4"],"2"];

var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);

console.log(flattened)

  • только для многомерного массива строк/чисел (не объекты)

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 функция с резервным вариантом ES3 для n-кратных вложенных массивов:

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

похоже, это похоже на работу для рекурсии!

  • обрабатывает несколько уровней вложенности
  • обрабатывает пустые массивы и не массив параметров
  • не имеет мутации
  • не полагается на современные функции браузера

код:

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

использование:

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

просто лучшее решение без лодашь

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

подход 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);

Это не сложно, просто повторите массивы и объедините их:

var result = [], input = [[""], [""], [""], [""], [""]];

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

логика здесь заключается в преобразовании входного массива в строку и удалении всех скобок ([]) и анализе вывода в массив. Я использую функцию шаблона ES6 для этого.

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)

я дурачился с генераторы ES6 на днях и писал в этом суть. Который содержит...

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

в основном я создаю генератор, который зацикливается на исходном входном массиве, если он находит массив, который использует доходность* оператор в сочетании с рекурсией для постоянного выравнивания внутренних массивов. Если элемент не является массивом, он просто доходность один элемент. Затем с помощью ES6 распространение оператор (aka splat operator) я сглаживаю генератор в новый экземпляр массива.

Я не проверял работоспособность, но я считаю, что это хороший простой пример использования генераторов и доходность* опе.

но опять же, я просто дурачился, поэтому я уверен, что есть более эффективные способы сделать это.

Я предлагаю два коротких решения без рекурсии. Они не являются оптимальными с точки зрения вычислительной сложности, но отлично работают в средних случаях:

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

на запрос, разбивая одну строку в основном имеет это.

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-->функции генератора:

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

при желании создайте массив сглаженных значений следующим образом:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]