配列の配列をマージ/フラット化する


1104

私は次のようなJavaScript配列を持っています:

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

個別の内部配列を次のようなものにマージするにはどうすればよいですか?

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

gist.github.com/Nishchit14/4c6a7349b3c778f7f97b912629a9f228このリンクは、ES5とES6のフラット化を説明しています
Nishchit Dhanani 2016年

17
reduce+ を使用するすべてのソリューションconcatはO((N ^ 2)/ 2)であり、受け入れられた回答(への1回の呼び出しconcat)は、不良ブラウザでは最大でO(N * 2)であり、いいもの。また、Denysソリューションは実際の質問に合わせて最適化されており、シングルの最大2倍の速さですconcat。以下のためのreduce人々の小さなコードを書いて冷却感じることの楽しさが、例えば、配列はすべて+、連結ソリューションをやっているだろう削減1000個の1つの素子サブアレイを持っていた場合は500500回の操作を、単一の連結または単純なループが1000回の操作を行うだろうとどこ。
gman 2017

12
単にユーザースプレッドオペレーター[].concat(...array)
Oleg Dater

@gmanはどのように削減+連結ソリューション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);

applyメソッドを使用するconcatと、2番目のパラメータが配列として取得されるため、最後の行は次のようになります。

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

Array.prototype.flat()配列をフラット化するために使用できる(ES2019で導入された)メソッドもありますが、これはバージョン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);
ネイト

65
var merged = [].concat.apply([], arrays);一行でそれを取得するために正常に動作するようです。編集:ニキータの答えがすでに示しているように。
Sean

44
またはArray.prototype.concat.apply([], arrays)
danhbear 2014年

30
注:この回答は1レベルの深さのみを平坦化します。再帰的なフラット化については、@ Trindazの回答を参照してください。
Phrogz

209
さらにショーンさんのコメント@へ:ES6の構文は、このスーパー簡潔になります:var merged = [].concat(...arrays)
セティ

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 '28

7
、それは減らす機能の開始アキュムレータ値@ayjayだ、何MDNは初期値を呼び出します。この場合flatは、に渡される無名関数への最初の呼び出しの値ですreduce。これが指定されていない場合、への最初の呼び出しreduceにより、配列の最初の値がにバインドされます。flatこれにより、両方の例で最終的に1バインドさflatれます。 1.concat関数ではありません。
Noah Freitas、2015年

19
または短くてセクシーな形式: const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
Tsvetomir Tsonev

12
任意のネストのため@TsvetomirTsonevとノアのソリューションにリフ:const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
ウィル

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が提供するソリューションは、JSだけでなくCoffeeScriptにも適しています。
ライアンケネディ

5
ああ、私はあなたのエラーを見つけました。あなたの記法に角括弧の余分なペアがあります[].concat([1],[2,3],[4],...)
ライアンケネディ

21
私もあなたの間違いを見つけたと思います。...実際のコードではなく、いくつかの省略記号のドットです。
amoebe

3
ライブラリー関数を使用しても、命令型の「混乱」が少なくなるわけではありません。混乱はライブラリの中に隠されているだけです。明らかに、ライブラリを使用することは、独自の関数をローリングすることよりも優れていますが、この手法が「命令的混乱」を減らすと主張するのはかなりばかげています。
paldepind 2014年

204

これは、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);
}, []);

または、ES2015の場合:

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

js-fiddle

Mozillaドキュメント


1
@JohnS実際には、ターンごとに単一(配列)値へのフィードを減らします。証明?reduce()を取り出し、機能するかどうかを確認します。ここFunction.prototype.applyの()に代わるものです)(削減
アンドレ・Werlang

3
私も減少しますが、私はこのスニペットから学んだ1つの、興味深いものがあることを使用していた時間のほとんどは、あなたがはinitialValue渡す必要はありません :)
ホセ・F. Romaniello

2
reduceの問題は、配列を空にできないため、追加の検証を追加する必要があることです。
calbertts

4
@calbertts初期値[]を渡すだけで、それ以上の検証は必要ありません。

8
ES6を使用しているため、spread-operatorを配列リテラルとして使用することもできます。arrays.reduce((flatten, arr) => [...flatten, ...arr])
Putzi San

109

これを正確に行うために、flatと呼ばれる新しいネイティブメソッドがあります。

(2019年末flat現在、ECMA 2019標準で公開されており、core-js@3(babelのライブラリ)はそれをpolyfill ライブラリに含めています

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.

そして、developer.mozilla.org
en

2
-1; いいえ、これはECMAScript 2018の一部ではありません。それでも、ECMAScript仕様に達していない提案にすぎません。
Mark Amery

1
私はこれをこれで検討できると思います..これは標準(2019)の一部なので、これのパフォーマンスの部分を一度再訪できますか?
Yuvaraj

ただし、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]));

巨大な配列をうまく処理します。私のマシンでは、このコードの実行に約14ミリ秒かかります。

ネストされた配列

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 2017年

@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表記の複雑さは何ですか?
ジョージカタノス2017

58

更新:このソリューションは大きな配列では機能しないことがわかりました。あなたがより良い、より速い解決策を探しているなら、この答えをチェックしてください。


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

isは単に展開されarr、それを引数としてに渡しconcat()ます。これにより、すべての配列が1つにマージされます。と同等[].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()and矢印関数などのメソッドはすべてのブラウザでサポートされているわけではありませんが、現在これらの機能を使用できないわけではありません。Babelを使用するだけで、ES6コードがES5に変換されます。


ここでの返信のほとんどすべてapplyがこのように誤用されているため、私はあなたのコメントを削除しました。私はまだapplyこの方法で/ spread を使用することは悪いアドバイスですが、誰も気にしないので...

@ LUH3417それはそうではありません、私は本当にあなたのコメントに感謝します。確かに、このソリューションは大規模な配列では機能しません。200 000要素の配列でも正常に機能する別の回答を投稿しました。
のMichałPerłakowski

:あなたはES6を使用している場合は、にさらにリデューサーすることができますconst flatten = arr => [].concat(...arr)
Eruant

「大きな配列では機能しない」とはどういう意味ですか?なんて大きい?何が起こるのですか?
GEMI 2018

4
@GEMIたとえば、このメソッドを使用して500000要素の配列をフラット化しようとすると、「RangeError:最大呼び出しスタックサイズを超えました」と表示されます。
のMichałPerłakowski

51

Underscoreを使用できます。

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

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

20
誰か、JSにPerlを追加しましたか?:-)
JBRウィルキンソン2013年

9
@JBRウィルキンソンたぶんJSはあなたにHaskell for Great Goodを学びたいと思っています!?
スタイフル2013年

1+-2 番目の引数を指定することによりtrue、浅いフラット化配列が必要であることを指定することもできます
Josh Crozier 2015年

7
@styfleのコメントを完全にするために:learnyouahaskell.com
Simon 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))

先見の明

そして、はい、あなたはそれを正しく推測しました、それは正確にそれがすべき方法である1つのレベルを平らにするだけです仕事を

このようなデータセットを想像してください

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

そこ。これで、ジョブごとに1つのレベルのネストを押しつぶすためのツールとflatten、すべてのネストを消去するためのツールが手に入りました。deepFlatten

多分それを呼び出すことができるobliteratenuke、あなたが名前を好きではない場合deepFlatten


2回繰り返さないでください。

もちろん、上記の実装は巧妙で簡潔ですが、を使用した.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
concatJavaScriptの意味はHaskellの場合とは異なることに注意してください。Haskellのconcat[[a]] -> [a])はflattenJavaScript で呼び出され、foldr (++) [](Javascript:foldr(concat) ([])カリー関数を想定)として実装されます。JavaScript concatは奇妙な追加((++)Haskellの場合)で、[a] -> [a] -> [a]およびの両方を処理できa -> [a] -> [a]ます。

flatMapそれはまさにモナドconcatMapbindインスタンスなので、私はより良い名前があったと思いますlistconcatpMapとして実装されfoldr ((++) . f) []ます。Javascriptに翻訳されました:const flatMap = f => foldr(comp(concat) (f)) ([])。これはもちろん、を使用しない実装に似ていますcomp

そのアルゴリズムの複雑さは何ですか?
ジョージカタノス2017

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 2013年

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 '21 / 02/21

2番目の引数を手動で渡すと、これは壊れます。たとえば、これを試してください:flattenArrayOfArrays (arr, 10)またはこれflattenArrayOfArrays(arr, [1,[3]]);-2番目の引数が出力に追加されます。
Om Shankar、2015

2
この答えは完全ではありません!再帰の結果はどこにも割り当てられないため、再帰の結果は失われます
r3wt

1
@ r3wtは先に進み、修正を追加しました。あなたがする必要があるのは、あなたが維持している現在の配列rが実際に再帰からの結果を連結することを確認することです。
2017年

29

単一要素配列の配列をフラット化するには、ライブラリをインポートする必要はありません。単純なループが最も単純で最も効率的なソリューションです。

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

反対票を投じる:質問を読んでください。異議を唱えることは、別の問題に適していないため、行わないでください。このソリューションは、質問に対して最も速くて簡単です。


4
「もっと不可解なものを探してはいけない」と私は言うでしょう。^^

4
つまり...そのためにライブラリを使用するように勧めている人々を見ると...それはクレイジーです...
DenysSéguretJun

3
ああ、これだけのためにライブラリを使用するのはばかげているでしょう...しかしそれがすでに利用可能であるなら。

9
@dystroy forループの条件部分は、判読できません。それとも私だけですか?D
Andreas

4
それがどれほど不可解であるかは、実際には重要ではありません。このコードはこれを「平坦化」['foo', ['bar']]['f', 'bar']ます。
jonschlinkert 2014

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
私たちの場合、2番目の引数は必要ありません[[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デフォルトです。

[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私の考えは、それは不必要だということです。それは主にスタイルの問題です。私のコメントはもっと明確にすべきです。どちらか一方を使用する主なコーディング上の理由はありません。ただし、この関数は非矢印として見やすく、読みやすくなっています。内部関数は、よりコンパクトなため(もちろん、コンテキストが作成されないため)、矢印関数として役立ちます。矢印関数は、コンパクトで読みやすい関数を作成し、この混乱を回避するのに最適です。ただし、矢印以外の値で十分な場合は、実際に読みにくくなる可能性があります。他の人も反対するかもしれません!
スティーブンシンプソン

取得RangeError: Maximum call stack size exceeded
Matt Westlake

@マット、エラーを再現するために使用する環境を共有してください
diziaq


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 2016年

2
言語Aの機能を言語Bに実装するのが、プロジェクトの一部としてではなく、これを正確に実行するという唯一の目標である場合は、どこかで誰かが間違った方向に進んでいたことになります。それはあなたでしょうか?と一緒に行くだけで、3つの追加の関数いくつかの関数呼び出しが必要な理由const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);を視覚的なゴミチームメイトに説明する手間が省けます。
Daerdemandt 2017

3
@Daerdemandtしかし、それを別個の関数として書くと、おそらく他のコードでそれらを再利用できるようになります。
のMichałPerłakowski

@MichałPerłakowski複数の場所でそれらを使用する必要がある場合は、ホイールを再発明してこれらからパッケージを選択しないでください。文書化され、他の人々によってサポートされています。
Daerdemandt 2017年

12

ES6 One Line Flatten

lodash flattenunderscore flatten(shallow )を参照してください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 One Line Deep Flatten

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

2つ目の例は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

ハスケレスクのアプローチ

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に同意します。このスレッド全体で最もエレガントなソリューションを伝えます。
ニコラス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
反対票を投じた人は誰でも、その理由を説明してください。私はまともな解決策を探していて、すべての解決策の中でこれが最も気に入った。
匿名

2
@匿名技術的に質問の要件を満たしているため、反対票を投じませんでしたが、これは一般的なケースでは役に立たないかなり貧弱なソリューションであるためと考えられます。ここにもっと良い解決策がいくつあるかを考えると、これが複数の要素を持っている瞬間、またはそれらが文字列ではないときに壊れるので、誰かがこれを使うことはお勧めしません。
Thor84no

2
私はこの解決策が大好きです=)
Huei Tan

2
1つの文字列要素を持つ配列だけを処理するのではなく、この配列も処理します ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
alucic

私は自分でこの方法を発見しましたが、すでにどこかで文書化されているに違いないことを知っていたため、検索はここで終了しました。このソリューションの欠点は、数値やブール値などを文字列に強制変換すること[1,4, [45, 't', ['e3', 6]]].toString().split(',')[1,4, [45, 't', ['e3', 6], false]].toString().split(',')
です。----

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に似ていますが、spreadオペレーター...を使用してジェネレーターを反復処理します。
Red Pea

6

配列全体をそのままの形で文字列に変換しますが、他の回答とは異なりJSON.stringifytoString()メソッドを使用して使用せず、不要な結果を生成します。

その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アイテムの値を誤解しています。私はそのカンマを配列デリミタカンマとしてではなく値として意図的に配置し、私のソリューションの出力を他のすべてのソリューションよりも強調します"3,4"
vsync '11

1
私は誤解しました
pizzarob 2016年

これは私がこれまで見た中で最速の解決策のようです。@vsyncの落とし穴に気づいていますか(もちろん少し入れ子になっているという事実を除いて-ネストされた配列を文字列として扱います:D)
George Katsanos '26 / 09/26

@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()メソッドは比較的新しいです。ieなどの古いブラウザは、メソッドを実装していない可能性があります。すべてのブラウザーでコードを機能させる場合は、JSを古いバージョンにトランスパイルする必要がある場合があります。現在のブラウザーの互換性については、MD Webドキュメントを確認してください。


2
高次元の配列をフラット化するには、Infinity引数を指定してフラットメソッドを呼び出すだけです。このように: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] 

注意深く、私のdevToolsを10秒間flatten(new Array(15000).fill([1]))投げUncaught RangeError: Maximum call stack size exceededて凍結しました
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
シンプルで甘い、この答えは受け入れられた答えよりもうまく機能します。それだけではなく、最初のレベルに深くネストされたレベルを平坦化する
オムShankarの

1
語彙のスコープであり、クロージャではない
AFAIK

@dashamblesは正しい-違いは、それがクロージャである場合は、内部関数を外部に返し、外部関数が終了しても、内部関数を使用してスコープにアクセスできることです。ここで、外部関数の寿命は内部関数の寿命よりも長いため、「クロージャ」は作成されません。
Mörre

5

先日、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);

基本的に、元の入力配列をループするジェネレーターを作成します。配列が見つかった場合は、内部配列を継続的にフラット化するために、recursionと組み合わせてyield *演算子を使用します。項目が配列でない場合は、単一の項目を生成するだけです。次に、ES6 Spreadオペレーター(別名splatオペレーター)を使用して、ジェネレーターを平坦化して新しい配列インスタンスにします。

私はこれのパフォーマンスをテストしていませんが、ジェネレーターとyield *演算子を使用する素晴らしいシンプルな例だと思います。

しかし、繰り返しますが、私はただふざけていたので、これを実行するより多くのパフォーマンスの高い方法があると確信しています。


1
同様の答え(ジェネレーターとyield委譲を使用)、ただしPHP形式
The Red Pea

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.