배열 배열 병합 / 평평하게하기


1104

다음과 같은 JavaScript 배열이 있습니다.

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

별도의 내부 배열을 다음과 같이 병합하는 방법은 무엇입니까?

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


17
reduce+ 를 사용하는 모든 솔루션 concat은 O ((N ^ 2) / 2)이며 여기서 허용되는 답변 (한 번만 호출 concat)은 잘못된 브라우저에서 최대 O (N * 2)이고 좋은 것. 또한 거부 솔루션은 실제 질문에 최적화되어 있으며 단일 질문보다 최대 2 배 빠릅니다 concat. 를 들어 reduce사람들 그것의 재미 배열이 모두 감소 + CONCAT 솔루션을하고있을 것 천 하나 개 개의 요소 서브 어레이가 있다면 멋진 작은 코드를 작성하지만, 예를 들어 느낌 500,500 작업을 어디에서 1000 작업을 할 것입니다 단일 CONCAT 또는 단순 루프있다.
gman

12
단순히 사용자 스프레드 운영자[].concat(...array)
Oleg Dater

@gman reduce + concat 솔루션 O ((N ^ 2) / 2)는 어떻습니까? stackoverflow.com/questions/52752666/… 은 다른 복잡성을 언급합니다.
Usman

4
ES2019 를 지원하는 최신 브라우저 : 평탄화 할 최대 깊이는 array.flat(Infinity)어디 입니까Infinity ?
디모데 구

답변:


1860

concat배열을 병합 하는 데 사용할 수 있습니다 .

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

console.log(merged);

applyof 메소드를 사용하면 concat두 번째 매개 변수를 배열로 사용하므로 마지막 줄은 다음과 같습니다.

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

Array.prototype.flat()배열을 평평하게하는 데 사용할 수 있는 방법 (ES2019에 도입 됨)도 있지만 Internet Explorer에서는 버전 11부터 Node.js에서만 사용할 수 있지만 Internet Explorer에서는 전혀 사용할 수 없습니다 .

const arrays = [
      ["$6"],
      ["$12"],
      ["$25"],
      ["$25"],
      ["$18"],
      ["$22"],
      ["$10"]
    ];
const merge3 = arrays.flat(1); //The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
console.log(merge3);
    


10
참고 concat소위, 원본 배열은 수정되지 않습니다 merged배열에 대한 호출 후 빈 유지됩니다 concat. 다음과 같이 말하는 것이 merged = merged.concat.apply(merged, arrays);
Nate

65
var merged = [].concat.apply([], arrays);한 줄로 가져 오는 것이 잘 작동하는 것 같습니다. 편집 : Nikita의 답변이 이미 표시되어 있습니다.
Sean

44
또는 Array.prototype.concat.apply([], arrays).
danhbear

30
참고 :이 답변은 한 수준 깊이 만 평평하게합니다. 재귀 평면화에 대해서는 @Trindaz의 답변을 참조하십시오.
Phrogz

209
@Sean의 의견에 더하여 : ES6 문법은 이것을 매우 간결하게 만듭니다 :var merged = [].concat(...arrays)
Sethi

505

다음은 최신 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]

17
나는이 접근법을 좋아한다. 훨씬 일반적이며 중첩 배열을 지원합니다.
alfredocambera

10
이 솔루션의 메모리 사용 프로필은 무엇입니까? 꼬리 재귀 동안 많은 중간 배열을 만드는 것처럼 보입니다 ....
JBRWilkinson

7
@ayjay, 그것이 mdn을 초기 값이라고 부르는 reduce 함수의 시작 누적 기 값입니다 . 이 경우에 flat전달 된 익명 함수에 대한 첫 번째 호출 의 값입니다 reduce. 지정되지 않은 경우 첫 번째 호출 reduce은 배열의 첫 번째 값 을에 바인딩하여 flat결국 두 예제 모두 1에 바인딩됩니다 flat. 1.concat기능이 아닙니다.
노아 프라이 타스

19
또는 더 짧고 더 섹시한 형태 : const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
Tsvetomir Tsonev

12
임의의 중첩을위한 @TsvetomirTsonev 및 Noah의 솔루션에 대한 분석 :const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
Will

314

혼란스럽게 숨겨져있는 메소드가 있는데,이 메소드는 원래 배열을 변경하지 않고 새 배열을 구성합니다.

var oldArray = [[1],[2,3],[4]];
var newArray = Array.prototype.concat.apply([], oldArray);
console.log(newArray); // [ 1, 2, 3, 4 ]


11
CoffeeScript에서 이것은[].concat([[1],[2,3],[4]]...)
amoebe

8
@amoebe 당신의 대답은 [[1],[2,3],[4]]결과로 제공합니다 . @Nikita가 제공하는 솔루션은 CoffeeScript뿐만 아니라 JS에도 적합합니다.
Ryan Kennedy

5
아, 오류가 발견되었습니다. 표기법에 여분의 대괄호 쌍이 있어야합니다 [].concat([1],[2,3],[4],...).
Ryan Kennedy

21
나는 당신의 오류도 발견했다고 생각합니다. 는 ...실제 코드가 아닌 일부 생략 점이다.
amoebe

3
라이브러리 함수를 사용한다고해서 덜 중요한 "메스"가있는 것은 아닙니다. 단지 엉망이 라이브러리 안에 숨겨져 있습니다. 분명히 라이브러리를 사용하는 것이 자신의 함수를 롤링하는 것보다 낫지 만,이 기법이 "구제적인 혼란"을 줄인다는 주장은 꽤 어리 석습니다.
paldepind

204

자바 스크립트 감소 기능으로 가장 잘 수행 할 수 있습니다.

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

또는 ES2015와 함께 :

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

JS 바이올린

모질라 문서


1
@JohnS 실제로 턴당 하나의 (배열) 값을 concat으로 줄입니다. 증명? reduce ()를 꺼내어 작동하는지 확인하십시오. reduce () 여기 Function.prototype.apply ()의 대안입니다
André Werlang

3
나뿐만 아니라 감소하지만이 조각에서 배운 하나 개 흥미로운 것은이 때문이다 사용했을 대부분의 시간을 당신이로 초기 통과 할 필요가 없습니다 :)
호세 F. 열로 Romaniello

2
reduce의 문제점은 배열을 비울 수 없으므로 추가 유효성 검사를 추가해야한다는 것입니다.
calbertts

4
@calbertts 초기 값 만 전달하면 []추가 검증이 필요하지 않습니다.

7
ES6을 사용 하므로 spread-operator를 array literal으로 사용할 수도 있습니다 . arrays.reduce((flatten, arr) => [...flatten, ...arr])
Putzi San

108

이를 위해 flat 이라는 새로운 기본 메소드가 있습니다 .

(2019 년 말 flat현재 ECMA 2019 표준으로 출판되었으며, core-js@3(바벨 라이브러리)는 해당 폴리 필 라이브러리 에 포함합니다 )

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

// Flatten 2 levels deep
const arr3 = [2, 2, 5, [5, [5, [6]], 7]];
arr3.flat(2);
// [2, 2, 5, 5, 5, [6], 7];

// Flatten all levels
const arr4 = [2, 2, 5, [5, [5, [6]], 7]];
arr4.flat(Infinity);
// [2, 2, 5, 5, 5, 6, 7];

4
첫 번째 답변 페이지조차도 수치스럽지 않습니다. 이 기능은 Chrome 69 및 Firefox 62 (백엔드에서 작업하는 노드 11)에서 사용할 수 있습니다.
Matt M.


2
-1; 아니요, 이것은 ECMAScript 2018의 일부가 아닙니다 . ECMAScript 사양에 적용되지 않은 제안 일뿐입니다.
Mark Amery

1
이제 우리는 이것을 고려할 수 있다고 생각합니다. 이제 표준 (2019)의 일부이기 때문에이 부분의 성능 부분을 다시 방문 할 수 있습니까?
Yuvaraj 's

그래도 Microsoft 브라우저에서 아직 지원하지 않는 것 같습니다 (적어도이 의견을 쓸 때)
Laurent S.

78

여기의 대부분의 답변은 거대한 (예 : 200,000 요소) 배열에서는 작동하지 않으며 그렇게하더라도 느립니다. polkovnikov.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]));

거대한 배열을 잘 처리합니다. 내 컴퓨터 에서이 코드는 실행하는 데 약 14ms가 걸립니다.

중첩 배열

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

중첩 배열과 함께 작동합니다. 이 코드는를 생성합니다 [1, 1, 1, 1, 1, 1, 1, 1].

다른 수준의 중첩을 가진 배열

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

이와 같은 배열을 병합하는 데 아무런 문제가 없습니다.


거대한 배열을 제외하고는 꽤 평평합니다. 이 솔루션은 깊게 중첩 된 배열에는 작동하지 않습니다. 재귀 솔루션은 없습니다. 실제로 브라우저는 없지만 Safari에는 현재 TCO가 있으므로 재귀 알고리즘이 제대로 작동하지 않습니다.
nitely

@nitely 그러나 어떤 실제 상황에서 몇 단계 이상의 중첩 배열이 있습니까?
Michał Perłakowski

2
일반적으로 사용자 생성 컨텐츠에서 배열을 생성하는 경우
nitely

@ 0xcaff Chrome에서는 200,000 요소 배열에서 전혀 작동하지 않습니다 (얻습니다 RangeError: Maximum call stack size exceeded). 20 000 요소 배열의 경우 2-5 밀리 초가 걸립니다.
Michał Perłakowski

3
이것의 O 표기법 복잡성은 무엇입니까?
George Katsanos

58

업데이트 :이 솔루션은 대형 배열에서 작동하지 않는 것으로 나타났습니다. 더 빠르고 더 나은 솔루션을 찾고 있다면 이 답변을 확인하십시오 .


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()및 화살표 기능 과 같은 방법 이 모든 브라우저에서 지원되는 것은 아니지만 지금이 기능을 사용할 수는 없습니다. Babel 만 사용 하면 ES6 코드가 ES5로 변환됩니다.


여기의 거의 모든 답변 apply이 이런 식으로 오용되기 때문에 귀하의 의견을 삭제했습니다. 나는 아직도 apply/ spread를 이런 식으로 사용 하는 것이 좋지 않다고 생각 하지만 아무도 신경 쓰지 않기 때문에 ...

@ LUH3417 그런식이 아닙니다. 여러분의 의견에 정말 감사합니다. 당신이 옳다는 것이 밝혀졌습니다.이 솔루션은 실제로 대형 배열에서는 작동하지 않습니다. 200,000 요소의 배열에서도 잘 작동하는 다른 답변 을 게시했습니다 .
Michał Perłakowski

ES6를 사용하는 경우 다음과 같이 줄일 수 있습니다.const flatten = arr => [].concat(...arr)
Eruant

"대형 배열에서 작동하지 않는다"는 것은 무엇을 의미합니까? 얼마나 커? 무슨 일이야?
GEMI

4
@GEMI 예를 들어이 메서드를 사용하여 500000 요소 배열을 병합하려고하면 "RangeError : Maximum call stack size exceeded"가 발생합니다.
Michał Perłakowski

51

밑줄 을 사용할 수 있습니다 :

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

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

20
Whoah, 누군가 Perl을 JS에 추가 했습니까? :-)
JBR 윌킨슨

9
@JBRWilkinson 어쩌면 JS가 큰 이익을 위해 Haskell을 배우고 싶을까요?
styfle

1+- 두 번째 인수를 지정true 하여 얕은 평평한 배열을 원하도록 지정할 수도 있습니다 .
Josh Crozier

7
: styfle의 코멘트 @에 대한 완성도를 위해서 learnyouahaskell.com
사이먼 A. Eugster

41

일반적인 절차는 특정 행동을 사용해야 할 때마다 복잡성을 다시 작성할 필요가 없음을 의미합니다.

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 =
  [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

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 ]


자주, 당신의 답장을 볼 때 나는 무가치 해 졌기 때문에 철회하고 싶습니다. 좋은 답변입니다! concat자체 만 스택을 폭파하지 않습니다 ...apply(매우 큰 배열과 함께)한다. 나는 그것을 보지 못했다. 나는 지금 끔찍하다고 느낀다.

@ LUH3417 끔찍한 느낌이 들지 않아야합니다. 또한 잘 작성된 설명과 함께 좋은 답변을 제공합니다. 여기에있는 모든 것은 학습 경험입니다. 저는 종종 실수를하고 나쁜 답변을 작성합니다. 몇 달 후에 다시 방문해서 "wtf가 생각하고 있었나요?" 결국 완전히 수정하게됩니다. 완벽한 답변은 없습니다. 우리는 학습 곡선에 어떤 시점에서 모든이야 ^ _ ^
감사합니다

1
주의하시기 바랍니다 concat자바 스크립트 하스켈과는 다른 의미를 갖습니다. Haskell의 concat( [[a]] -> [a])는 flattenJavascript 로 호출 되며 foldr (++) [](Javascript : foldr(concat) ([])커리 함수 가정 )으로 구현됩니다 . 자바 스크립트 concat는 이상한 추가 ( (++)Haskell에서)이며 [a] -> [a] -> [a]와를 모두 처리 할 수 ​​있습니다 a -> [a] -> [a].

나는 그것이 더 나은 이름이라고 생각합니다 flatMap. 왜냐하면 모나드 concatMapbind인스턴스입니다 list. concatpMap로 구현됩니다 foldr ((++) . f) []. 자바 스크립트로 번역 : const flatMap = f => foldr(comp(concat) (f)) ([]). 이것은 물론 당신의 구현과 비슷합니다 comp.

그 알고리즘의 복잡성은 무엇입니까?
George Katsanos

32

배열에 배열이 아닌 요소가있을 수있는보다 일반적인 경우에 대한 솔루션.

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

4
이 접근 방식은 JsonPath 쿼리 에서 얻은 중첩 된 배열 형식의 결과 집합을 병합하는 데 매우 효과적이었습니다 .
kevinjansz

1
배열 방법으로 추가 :Object.defineProperty(Array.prototype,'flatten',{value:function(r){for(var a=this,i=0,r=r||[];i<a.length;++i)if(a[i]!=null)a[i] instanceof Array?a[i].flatten(r):r.push(a[i]);return r}});
Phrogz

두 번째 인수를 수동으로 전달하면 중단됩니다. 예를 들어 this : flattenArrayOfArrays (arr, 10)또는 this를 시도해보십시오. flattenArrayOfArrays(arr, [1,[3]]);두 번째 인수가 출력에 추가됩니다.
옴 샨카

2
이 답변은 완료되지 않았습니다! 재귀의 결과는 어디에도 할당되지 않으므로 재귀의 결과가 손실됩니다
r3wt

1
@ r3wt는 계속해서 수정을 추가했습니다. 해야 할 일은 유지 관리중인 현재 배열 r이 실제로 재귀의 결과를 연결 하는지 확인하는 것입니다.
aug

29

사용 reduce(callback[, initialValue])방법은 어떻습니까JavaScript 1.8

list.reduce((p,n) => p.concat(n),[]);

일을 할 것입니다.


8
[[1], [2,3]].reduce( (a,b) => a.concat(b), [] )더 섹시합니다.
golopot

5
우리의 경우 간단한 두 번째 주장이 필요하지 않습니다[[1], [2,3]].reduce( (a,b) => a.concat(b))
Umair Ahmed

29

기능적 스타일의 또 다른 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]

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


console.log( flatten([1, [2,3], [4,[5],[6,[7,8,9],10],11],[12],13]) )

최신 브라우저의 최신 릴리스에서 사용할 수 있는 기본 함수 Array.prototype.flat () (ES6 용 제안 ) 도 고려하십시오 . @ (Константин Ван)와 @ (Mark Amery) 덕분에 의견에서 언급했습니다.

flat함수에는 배열 중첩의 예상 깊이를 지정하는 하나의 매개 변수가 있으며 1기본적으로 동일 합니다.

[1, 2, [3, 4]].flat();                  // -> [1, 2, 3, 4]

[1, 2, [3, 4, [5, 6]]].flat();          // -> [1, 2, 3, 4, [5, 6]]

[1, 2, [3, 4, [5, 6]]].flat(2);         // -> [1, 2, 3, 4, 5, 6]

[1, 2, [3, 4, [5, 6]]].flat(Infinity);  // -> [1, 2, 3, 4, 5, 6]

let arr = [1, 2, [3, 4]];

console.log( arr.flat() );

arr =  [1, 2, [3, 4, [5, 6]]];

console.log( arr.flat() );
console.log( arr.flat(1) );
console.log( arr.flat(2) );
console.log( arr.flat(Infinity) );


3
이것은 훌륭하고 깔끔하지만 ES6 과다 복용을했다고 생각합니다. 외부 기능이 화살표 기능 일 필요는 없습니다. 콜백 감소를 위해 화살표 기능을 사용하지만 병합 자체는 정상적인 기능이어야합니다.
Stephen Simpson

3
@StephenSimpson 외부 함수가 화살표 가 아닌 함수 일 필요가 있습니까? "평면 자체는 정상적인 기능이어야합니다" – "정상"이라는 말은 "화살표가 아닌"을 의미하지만 왜 그럴까요? 통화에서 화살표 기능을 사용하여 축소하는 이유는 무엇 입니까? 당신은 당신의 추론을 제공 할 수 있습니까?
감사합니다.

@naomik 내 추론은 불필요하다는 것입니다. 주로 스타일의 문제입니다. 내 의견에 훨씬 명확해야합니다. 하나 또는 다른 것을 사용해야 할 주요 코딩 이유는 없습니다. 그러나이 기능은 화살표가 아닌 것으로보고 쉽게 읽을 수 있습니다. 내부 함수는 더 작고 (물론 컨텍스트가 생성되지 않으므로) 화살표 함수로 유용합니다. 화살표 기능은 작고 읽기 쉬운 기능을 만들고 이러한 혼동을 피하는 데 유용합니다. 그러나 비 화살표로 충분할 때 실제로 읽기가 더 어려워 질 수 있습니다. 다른 사람들은 동의하지 않을 수 있습니다!
Stephen Simpson

받기RangeError: Maximum call stack size exceeded
Matt Westlake

@ 매트, 오류를 재현하는 데 사용하는 evnironment를 공유하십시오
diziaq

28

단일 요소 배열의 배열을 평면화하려면 라이브러리를 가져올 필요가 없습니다. 간단한 루프는 가장 간단하고 효율적인 솔루션입니다.

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

downvoters : 질문을 읽고, 매우 다른 문제에 맞지 않기 때문에 downvote하지 마십시오. 이 솔루션은 질문에 가장 빠르고 간단합니다.


4
나는 "보다 비밀스러운 것을 찾지 마라"고 말할 것입니다. ^^

4
내 말은 .. 사람들이 도서관을 이용하라고 조언하는 것을 보았을 때 ... 그건 미친 짓이야 ...
Denys Séguret

3
아, 라이브러리를 사용하는 바로 이 바보 것이기 ...하지만 이미 사용할 수있는 경우.

9
@dystroy for 루프의 조건부 부분은 읽을 수 없습니다. 아니면 그냥 나야 : D
Andreas

4
그것이 얼마나 비밀 스러운지는 중요하지 않습니다. 이 코드는 이것을 "평평하게" ['foo', ['bar']]합니다 ['f', 'bar'].
jonschlinkert

22
const common = arr.reduce((a, b) => [...a, ...b], [])

이것은 내가 내 코드를 통해 그것을 어떻게하지만 난 당신이 매우 긴 배열이있는 경우 속도가 허용 답변을 비교하는 방법 확실하지 않다
마이클 아론 윌슨

14

제발 참고 하면 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));

커리 양식, 함수 구성 및 고차 함수의 작은 화살표 함수에 익숙해 지 자마자이 코드는 산문처럼 읽습니다. 프로그래밍은 부작용을 포함하지 않기 때문에 항상 예상대로 작동하는 작은 빌딩 블록을 구성하는 것입니다.


1
ㅋ. 이와 같은 기능적 프로그래밍을 읽는 것은 여전히 ​​일본어 문자를 나에게 읽는 것과 같습니다 (영어 사용자).
Tarwin Stroh-Spijer

2
정확히 이것을 할 목적으로 프로젝트의 일부가 아닌 언어 B에서 언어 A의 기능을 구현하고 있다면 어딘가에서 누군가가 잘못 돌았을 것입니다. 당신이 될 수 있습니까? 그냥 가면서 const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);시각적 쓰레기 설명을 3 가지 추가 기능 일부 함수 호출 이 필요한 이유를 팀원에게 저장합니다 .
Daerdemandt

3
@Daerdemandt 그러나 별도의 함수로 작성하면 다른 코드에서 재사용 할 수 있습니다.
Michał Perłakowski

MichałPerłakowski @ 당신은 바퀴를 재발견하지 않는 다음 여러 장소에서 그들을 사용에서 패키지를 선택해야하는 경우 다음을 - 다른 사람에 의해 문서화 및 지원.
Daerdemandt

12

ES6 한 줄 평평

참조 lodash 펼치기 , 밑줄 펼치기 (얕은를 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 한 줄 깊이 평평

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

두 번째 예제는 함수 Array.prototype.concat.apply([], arr)에 도달하기 위해 추가 배열을 작성하기 때문에 더 잘 작성되었습니다 concat. 런타임은 실행할 때 최적화하지 않을 수도 있지만 프로토 타입에서 함수에 액세스하는 것은 이미 어떤 경우보다 더 나쁘게 보이지 않습니다.
Mörre

11

당신은 사용할 수 있습니다 Array.flat()Infinity중첩 된 배열의 깊이.

var arr = [ [1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2,3,4,[1,2,3,4]]], [[1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2,3,4,[1,2,3,4]]]] ];

let flatten = arr.flat(Infinity)

console.log(flatten)

브라우저 호환성에 대해서는 여기를 확인하십시오


8

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


1
@monners에 동의하십시오. 이 전체 스레드에서 가장 우아한 솔루션을 제공합니다.
Nicolás Fantone

8

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

flattenN 배 중첩 배열에 대한 ES3 폴백 기능을 위한 ES5 방법 :

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


7

문자열 요소가 1 개인 배열 만있는 경우 :

[["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(',');

일을 할 것입니다. Bt는 코드 예제와 특별히 일치합니다.


3
아래로 투표 한 사람은 이유를 설명하십시오. 나는 알맞은 솔루션 과이 솔루션을 가장 좋아하는 모든 솔루션을 찾고있었습니다.
Anonymous

2
@Anonymous 나는 기술적으로 질문의 요구 사항을 충족시키기 때문에 그것을 하향 투표하지는 않았지만, 이것은 일반적인 경우에는 유용하지 않은 꽤 가난한 솔루션이기 때문에 가능합니다. 여기에 몇 가지 더 나은 솔루션이 있는지 고려할 때 하나 이상의 요소가있는 순간이나 문자열이 아닌 경우 누군가 가이 솔루션을 사용하는 것은 결코 권장하지 않습니다.
Thor84no

2
나는이 솔루션을 사랑 =)
Huei 탄

2
문자열 요소가 1 개인 배열 만 처리하지 ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
않고이

나는이 방법을 스스로 발견했지만 이미 어딘가에 문서화되어 있어야한다는 것을 알고 내 검색은 여기서 끝났습니다. 이 솔루션의 단점은 숫자, 부울 등을 문자열로 강제 변환하는 것입니다. [1,4, [45, 't', ['e3', 6]]].toString().split(',') ---- 또는 ----- [1,4, [45, 't', ['e3', 6], false]].toString().split(',')
Sudhansu Choudhary

7
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

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

(@danhbear의 의견에 따라 별도의 답변으로 작성하고 있습니다.)


7

공간 효율적인 생성기 기능을 권장 합니다 .

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]

나는이 접근법을 좋아한다. stackoverflow.com/a/35073573/1175496 과 유사 하지만 스프레드 연산자 ...를 사용하여 생성기를 반복합니다.
The Red Pea

6

오히려 전체 배열을 그대로 문자열로 변환하지만 다른 답변과 달리 원하지 않는 결과를 생성하는 메서드 를 사용 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)

  • 문자열 / 숫자 (객체 아님)의 다차원 배열에만 해당

해결책이 잘못되었습니다. 내부 배열을 병합 할 때 ["345", "2", "3,4", "2"]각 값을 분리하여 인덱스를 구분하는 대신 쉼표를 포함합니다.
pizzarob

@realseanp-해당 배열 항목의 값을 잘못 이해했습니다. 나는 의도적으로 그 쉼표를 Array delimiter 쉼표가 아닌 값으로 두어 다른 솔루션보다 내 솔루션의 힘을 강조하여 출력 "3,4"합니다.
vsync

1
나는 오해했다
pizzarob

그것은 내가 본 가장 빠른 해결책 인 것 같습니다. 당신은 어떤 함정 @vsync 알고 있습니다 (이것은 물론 약간의 해키을 보인다는 사실을 제외하고 - 문자열로 중첩 배열의 치료 : D)
조지 Katsanos

@GeorgeKatsanos -이 방법 것, 예를 들면 원시 값없는 배열 항목 (항목 및 중첩) 항목에 대한 작업하지 DOM 요소되는 포인트
VSYNC

6

새로운 Array.Flat()방법을 시도 할 수도 있습니다 . 다음과 같은 방식으로 작동합니다.

let arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]].flat()

console.log(arr);

flat()방법은 모든 하위 배열 요소를 깊이가 1 레이어까지 반복적으로 연결된 새 배열을 만듭니다 (예 : 배열 내부 배열).

3 차원 또는 더 높은 차원의 배열을 평면화하려면 flat 메소드를 여러 번 호출하면됩니다. 예를 들어 (3 차원) :

let arr = [1,2,[3,4,[5,6]]].flat().flat().flat();

console.log(arr);

조심해!

Array.Flat()방법은 비교적 새로운 방법입니다. 예를 들어 이전 브라우저는 메소드를 구현하지 않았을 수 있습니다. 코드를 모든 브라우저에서 작동 시키려면 JS를 이전 버전으로 변환해야 할 수도 있습니다. 최신 브라우저 호환성에 대해서는 MD 웹 문서를 확인하십시오.


2
더 높은 차원의 배열을 평면화하려면 Infinity인수로 flat 메소드를 호출하면 됩니다. : 이것처럼arr.flat(Infinity)
미하일

Mikhail에게 감사합니다.
Willem van der Veen

6

스프레드 연산자 사용 :

const input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
const output = [].concat(...input);
console.log(output); // --> ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]


5

그것은 어렵지 않습니다. 배열을 반복하고 병합하십시오.

var result = [], input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"]];

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

5

이것이 RECURSION의 직업처럼 보입니다!

  • 여러 수준의 중첩 처리
  • 빈 배열과 배열이 아닌 매개 변수를 처리합니다.
  • 돌연변이가 없다
  • 최신 브라우저 기능에 의존하지 않습니다

암호:

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] 

주의, flatten(new Array(15000).fill([1]))던졌습니다 Uncaught RangeError: Maximum call stack size exceeded10 초 동안 내 DevTools로를 얼어 붙었다와
pietrovismara

@pietrovismara, 내 테스트는 약 1 초입니다.
Ivan Yan

5

재귀와 클로저를 사용하여 수행했습니다.

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

1
간단하고 달콤합니다.이 답변은 허용 된 답변보다 더 효과적입니다. 그것은 깊이뿐만 아니라 첫 번째 수준에 수준 중첩 평평
옴 샨

1
어휘 범위 및 폐쇄가 아닌 AFAIK
dashamble 님이

@dashambles는 정확합니다. 차이점은 그것이 클로저 인 경우 내부 함수를 외부로 반환하고 외부 함수가 완료되면 여전히 내부 함수를 사용하여 범위에 액세스 할 수 있다는 것입니다. 여기서 외부 함수의 수명이 내부 함수의 수명보다 길기 때문에 "클로저"가 만들어지지 않습니다.
Mörre

5

나는 며칠 전에 ES6 Generators를 사용 하여이 글을 썼습니다 . 포함하는...

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

기본적으로 원래 입력 배열을 반복하는 생성기를 만들고 있는데, 배열을 찾으면 yield * 연산자를 재귀와 함께 사용하여 내부 배열을 계속 평탄화합니다. 항목이 배열이 아닌 경우 단일 항목 만 생성 됩니다. 그런 다음 ES6 Spread 연산자를 사용하십시오. (일명 splat 연산자)를 사용하여 생성기를 새 배열 인스턴스로 평면화했습니다.

나는 이것의 성능을 테스트하지는 않았지만 생성기와 yield * 연산자를 사용하는 좋은 예라고 생각합니다.

그러나 다시, 나는 방황하고 있었기 때문에 이것을 수행하는 더 효과적인 방법이 있다고 확신합니다.


1
비슷한 대답 (제너레이터와 수확량 위임 사용), 그러나 PHP 형식
The Red Pea

5

lodash가없는 최고의 솔루션

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.