Подтвердить что ты не робот

Как сгладить вложенный массив в javascript?

Как известно, сгладить массив [[0, 1], [2, 3], [4, 5]] с помощью метода reduce()

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
  return a.concat(b);
});

Итак, как сгладить этот массив [[[0], [1]], [[2], [3]], [[4], [5]]] до [0, 1, 2, 3, 4, 5]?

4b9b3361

Ответ 1

Это альтернатива рекурсии (см. jsfiddle здесь) и должна принимать любой уровень глубины, который позволяет избежать стека переполнение.

var array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];
console.log(flatten(array), array); // does not mutate array
console.log(flatten(array, true), array); // array is now empty

// This is done in a linear time O(n) without recursion
// memory complexity is O(1) or O(n) if mutable param is set to false
function flatten(array, mutable) {
    var toString = Object.prototype.toString;
    var arrayTypeStr = '[object Array]';
    
    var result = [];
    var nodes = (mutable && array) || array.slice();
    var node;

    if (!array.length) {
        return result;
    }

    node = nodes.pop();
    
    do {
        if (toString.call(node) === arrayTypeStr) {
            nodes.push.apply(nodes, node);
        } else {
            result.push(node);
        }
    } while (nodes.length && (node = nodes.pop()) !== undefined);

    result.reverse(); // we reverse result to restore the original order
    return result;
}

Ответ 2

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

function flatten(ary) {
    var ret = [];
    for(var i = 0; i < ary.length; i++) {
        if(Array.isArray(ary[i])) {
            ret = ret.concat(flatten(ary[i]));
        } else {
            ret.push(ary[i]);
        }
    }
    return ret;
}

flatten([[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]]) // [0, 1, 2, 3, 4, 5]

Альтернативно, как метод массива:

Array.prototype.flatten = function() {
    var ret = [];
    for(var i = 0; i < this.length; i++) {
        if(Array.isArray(this[i])) {
            ret = ret.concat(this[i].flatten());
        } else {
            ret.push(this[i]);
        }
    }
    return ret;
};

[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flatten() // [0, 1, 2, 3, 4, 5]

EDIT: Ну, подумайте, что это немного функциональный способ (за исключением названной рекурсии, которая должна использовать Y-combinator для чистого функционала: D).

function flatten(ary) {
  return ary.reduce(function(a, b) {
    if (Array.isArray(b)) {
      return a.concat(flatten(b))
    }
    return a.concat(b)
  }, [])
}

Возьмем некоторый синтаксис ES6, который делает его еще короче, в одной строке.

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

Но помните, что этот метод нельзя применять как метод массива, потому что функции стрелок не имеют своего собственного this.

Ответ 3

ES6-стиль с рекурсией:

function flatten(arr) {
  const flat = [].concat(...arr);
  return flat.some(Array.isArray) ? flatten(flat) : flat;
}

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


Июнь 2018 г. Обновление:

В настоящее время существует предложение ES для метода Array.prototype.flat. В настоящее время он находится на третьем этапе, что означает, что он скорее всего будет реализован браузерами (ish) и внесет их в спецификацию в ее текущей форме. Есть, вероятно, несколько полисов, плавающих вокруг.

Пример:

const nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
const flattened = nested.flat(2);  // Need to specify depth if > 1

Ответ 4

На основе решения @Leo, но быстрее, повторно используя тот же массив и предотвращая .concat

function flatten(ary, ret) {
    ret = ret === undefined ? [] : ret;
    for (var i = 0; i < ary.length; i++) {
        if (Array.isArray(ary[i])) {
            flatten(ary[i], ret);
        } else {
            ret.push(ary[i]);
        }
    }
    return ret;
}

Пример:

function flatten(ary, ret) {
  ret = ret === undefined ? [] : ret;
  for (var i = 0; i < ary.length; i++) {
    if (Array.isArray(ary[i])) {
      flatten(ary[i], ret);
    } else {
      ret.push(ary[i]);
    }
  }
  return ret;
}
console.log(flatten([[[0], [1]], [[2], [3]], [[4], [5]]]));

Ответ 5

ES6 однострочный:

function flatten(a) {
    return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
}

Кроме того, нерекурсивная версия для очень глубоких массивов (не очень эффективная, но довольно элегантная)

function flatten(a) {
    var queue = a.slice();
    var result = [];
    while(queue.length) {
        let curr = queue.pop();
        if(Array.isArray(curr)) {
            queue.push(...curr);
        }
        else result.push(curr);
    }
    return result;
}

Ответ 6

Только уровни Flattens 2:

var arr = [1, [2, 3], [4, 5, 6]];
[].concat.apply([], arr) // -> [1, 2, 3, 4, 5, 6]

Ответ 7

Мне нравится мое решение:)

var flattenClosure = function(a) {
    var store = [];

    return function() {
        var internMapper = function(b) {
            if (Array.isArray(b)) {
                return b.map(internMapper);
            }
            store.push(b);
            return b;
        }
        a.map(internMapper);
        return store;
    }
};

console.log(flattenClosure([[[[[[[[1]]]], [2], [4], [6, 8, 9], 2]]], 10, 11, [15, 17, 20], [], 33])());

Ответ 8

Вдохновленный кодом из Красноречивый JavaScript и ответ, предоставленный @axelduch (намного более эффективный из того, что я могу сказать тоже).

function flatten(array, mutable) {
  var nodes = (mutable && array) || array.slice(); // return a new array.
  var flattened = [];

  for (var node = nodes.shift(); node !== undefined; node = nodes.shift()) {
    if (Array.isArray(node)) {
      nodes.unshift.apply(nodes, node);
    } else {
      flattened.push(node);
    }
  }

  return flattened;
}

Ответ 9

Отказ от ответственности. Я знаю, что это старый и уже ответивший вопрос, но @Nick вовлек меня в это, поскольку я прокомментировал его ответ как один из самых дорогих способов сгладить массив. Я не кодировал JavaScript уже много лет, но мне нравится кататься на велосипеде - как только вы узнаете, что никогда не забудете;)

Вот мой полный рекурсивный код (нет цикла for):

var flattened = [];
function flatten(a, i) {
    if(a.length > i) {
        if(Array.isArray(a[i]))
            flatten(a[i], 0);
        else
            flattened.push(a[i]);
        flatten(a, i + 1);
    }
}

flatten([[0, 1], [2, 3], [4, 5]], 0);
console.log(flattened);

Я тестировал его против решения toString().split(','), а мой - примерно в 7 раз быстрее. Это то, что я имею в виду, говоря о затратности;)

Ответ 10

function flatten(array) {
    return array.reduce(
        (previous, current) =>
            Array.isArray(current)
            ? [...previous, ...flatten(current)]
            : [...previous, current]
        , []
    );
}

Ответ 11

Если вы знаете, что массив состоит из только чисел, вы можете просто сделать следующее:

array.join().split(',').map(Number);

Ответ 12

Это уже ответили, но я просто изучаю JS и задаюсь вопросом:

    var array = [[[0], [1]], [[2], [3]], [[4], [5]]];
    var flattend = array.join(",").split(",");
    console.log(flattend);

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

Ответ 13

В lodash есть три функции утилиты, связанные с вашим вопросом flatten, flattenDeep, flattenDepth в lodash. flatten переходит в одну глубину, flattenDeep проходит весь путь до самого глубокого уровня, а flattenDepth дает вам выбор "как глубоко" сгладить.

Пример:

> var arr = [[[0], [1]], [[2], [3]], [[4], [5]]];
> _.flattenDeep(arr)
   [0, 1, 2, 3, 4, 5]

Ответ 14

Реализация с функциональным программированием

С функциональным программированием мы можем просто вывести flatten из другой более общей функции: traverse.

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

const traverse = f => g => acc => xs => {
  let [leaf, stack] = xs[0][0] === undefined
   ? [xs[0], xs.slice(1)]
   : f([]) (xs);

  return stack.length
   ? traverse(f) (g) (g(leaf) (acc)) (stack)
   : g(leaf) (acc);
};

const dfs = stack => tree => tree[0] === undefined 
 ? [tree, stack]
 : dfs(tree.length > 1 ? concat(stack) (tree.slice(1)) : stack) (tree[0]);

const concat = ys => xs => xs.concat(ys);
const flatten = f => traverse(f) (concat) ([]);

const xs = [[[1,2,3],4,5,6],7,8,[9,10,[11,12],[[13]],14],15];

console.log(flatten(dfs) (xs));

Ответ 15

var nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
var flattened = [].concat.apply([],[].concat.apply([],nested));
console.log('-> flattened now: ' + flattened);

Ответ 16

function flatten(x) {
  if (x.length == 0) {return []};
  if (Array.isArray(x[0])) {
    return flatten(x[0].concat(flatten(x.slice(1,x.length))));
  }
  return [].concat([x[0]], flatten(x.slice(1,x.length)));
}

рекурсивно выравнивает массив.

Ответ 17

На основе dashamble answer, но я считаю, что это немного проще понять:

var steamroller = function(arr) {
  var result = [];

  var dropHeavyObject = function(auxArr) {
    var flatnd = [];

    flatnd = auxArr.map(function(x) {
      if(Array.isArray(x)) {
        return dropHeavyObject(x);
      } else {
        result.push(x);
        return x;
      }
    });

    return flatnd;
  };

  dropHeavyObject(arr);
  return result;
}

Ответ 18

Используя JSON.stringify и JSON.parse

arr = JSON.parse("[" + 
               JSON.stringify(arr)
                   .replace(/[\[\]]+/g,"")
                   .replace(/,,/g,",") +
               "]");

Ответ 19

Использование соглашений Lisp.

Использование .shift() и .concat() неэффективно.

    flatten (array) {

        // get first element (car) and shift array (cdr) 
        var car = array.shift();

        // check to see if array was empty
        if (car === undefined) {
            return [];

        // if the first element (car) was an array, recurse on it
        } else if (_.isArray(car)) {
            return flatten(car).concat(flatten(array));

        // otherwise, cons (concatenate) the car to the flattened version of cdr (rest of array)
        } else {
            return [car].concat(flatten(array))
        }
    }

Ответ 21

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


var arr0 = [0, 1, 2, 3, 4];
var arr1 = [[0,1], 2, [3, 4]];
var arr2 = [[[0, 1], 2], 3, 4];
console.log(flatten(arr0));  // [0, 1, 2, 3, 4]
console.log(flatten(arr1));  // [0, 1, 2, 3, 4]
console.log(flatten(arr2));  // [0, 1, 2, 3, 4]
console.log(flatten([]));    // []

Ответ 22

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

Если вы берете вложенный в строку массив, он разделяет значения запятыми. Затем вы можете разбить его запятыми, чтобы сформировать строку. Если вам нужно преобразовать строки в int или float, вы можете запустить новый массив, преобразуя каждое значение.

stringArray = [[0, 1], [2, 3], [4, 5]].toString().split(',');
stringArray.forEach((v,i,a) => a[i] = parseFloat(a[i]));

Ответ 23

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

const a = [[1,2,[3]],4]

Array.prototype.flatten = (array) => {
  const newArray = []
  const flattenHelper = (array) => {
    array.map(i => {
      Array.isArray(i) ? flattenHelper(i) : newArray.push(i)
    })
  }
  flattenHelper(a)
  return newArray
}

const newArray = a.flatten()
console.log(newArray);

Ответ 24

Вот что у меня есть:

function steamrollArray(arr) {
  // the flattened array
  var newArr = [];

  // recursive function
  function flatten(arr, newArr) {
    // go through array
    for (var i = 0; i < arr.length; i++) {
      // if element i of the current array is a non-array value push it
      if (Array.isArray(arr[i]) === false) {
        newArr.push(arr[i]);
      }
      // else the element is an array, so unwrap it
      else {
        flatten(arr[i], newArr);
      }
    }
  }

  flatten(arr, newArr);

  return newArr;
}

Ответ 25

Может быть решена как

const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];

const flatten(arr) => arr.reduce((acc, item) => 
   acc.concat(Array.isArray(item) ? flatten(item) : item);
}, []);

console.log(flatten(array));

Помните, что в случае глубоких массивов TCO следует применять

Версия с TCO с рекурсивным решением

const array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];

const flatten = (() => {
  const _flatten = (acc, arr) => arr.reduce((acc, item) =>  acc.concat(Array.isArray(item) ? _flatten([], item) : item), acc);

  return arr => _flatten([], arr);
})();

console.log(flatten(array))

Ответ 26

var flattenWithStack = function(arr) {
  var stack = [];
  var flat = [];
  stack.push(arr);
  while(stack.length > 0) {
    var curr = stack.pop();
    if(curr instanceof Array) {
      stack = stack.concat(curr);
    } else {
      flat.push(curr);
    }
  }
  return flat.reverse();
}

Нерекурсивный. В основном dfs.

Ответ 27

Думайте, что эта функция работает.

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

}