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

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

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

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

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

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.

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

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

или, с ES2015:

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

в JS-скрипка

Mozilla docs


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

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

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 и возвращает новую функцию уменьшения-этот комбинатор находится в основе датчики; если вам интересно,я написал другие ответы о их!--67-->

// 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: пожалуйста, прочитайте вопрос, не downvote, потому что он не подходит для вашей совсем другой проблемы. Это решение является самым быстрым и простым для заданного вопроса.


есть новый собственный метод 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 вывод, все, что осталось, - это удалить все скобки, снова обернуть результат начальными и конечными скобками и служить результату с 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] 

просто лучшее решение без lodash

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 в распространение оператор (Он же оператор splat) я выравниваю генератор в новый экземпляр массива.

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

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


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

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]