javascriptにArray.prototype.flatMapがないのはなぜですか?


82

flatMapコレクションで非常に便利ですが、javascriptはArray.prototype.map。を持っている間はそれを提供しません。どうして?

手動でflatMap定義せずに、簡単かつ効率的な方法でJavaScriptでエミュレートする方法はありますflatMapか?


10
"なぜ?" まだ誰も提案していないので?新機能の提案方法は次のとおりです「flatMapを手動で定義せずに... flatMapをエミュレートする方法はありますか?」え?わかりません。あなたは好きarr.reduce((arr, v) => (arr.push(...v), arr), [])ですか?
Felix Kling 2016年

(^それは平らな部分だけなので、そうあるべきだと思いますarr.map(...).reduce(...))。
Felix Kling 2016年

pingを実行した後、配列をフラット化することができ.mapます。
kennytm 2016年

1
うーん、あなたはそれを「エミュレート」したいが、それを「定義」したくない。それはどういう意味ですか?

8
間もなくJSの一部になる予定です。tc39.github.io/proposal-flatMap
Vijaiendran

回答:


99

更新: Array.prototype.flatMap ES2019になりました

多くの環境で広くサポートされています。以下のスニペットを使用して、ブラウザで機能するかどうかを確認してください-

const data =
  [ 1, 2, 3, 4 ]
  
console.log(data.flatMap(x => Array(x).fill(x)))
// [ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 ]


「JavaScriptにArray.prototype.flatMapがないのはなぜですか?」

プログラミングは魔法ではなく、すべての言語が他のすべての言語が持っている機能/プリミティブを持っているわけではないからです。重要なのは、JavaScriptがあなた自身でそれを定義する能力を与えることです-

const concat = (x,y) =>
  x.concat(y)

const flatMap = (f,xs) =>
  xs.map(f).reduce(concat, [])

const xs = [1,2,3]

console.log(flatMap(x => [x-1, x, x+1], xs))

または、2つのループを1つに折りたたむ書き直し-

const flatMap = (f, xs) =>
  xs.reduce((r, x) => r.concat(f(x)), [])

const xs = [1,2,3]

console.log(flatMap(x => [x-1, x, x+1], xs))

あなたがそれを拡張したいのならArray.prototype、何もあなたを止めていません-

if (!Array.prototype.flatMap) {
  function flatMap (f, ctx) {
    return this.reduce
      ( (r, x, i, a) =>
          r.concat(f.call(ctx, x, i, a))
      , []
      )
  }
  Array.prototype.flatMap = flatMap
}

const ranks =
  [ 'J', 'Q', 'K', 'A' ]
  
const suits =
  [ '♡', '♢', '♤', '♧' ]

const result =
  ranks.flatMap(r =>
    suits.flatMap(s =>
      [[r, s]]
    )
  )

console.log(JSON.stringify(result))
// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]


1
Array.prototype._mylib_flatMap単にを定義するのではなく、プロトタイプへの追加を名前空間化する(たとえば)か、ES6シンボルを使用する方がよいと考えられますArray.prototype.flatMap
Fengyang Wang

11
@FengyangWang 「よりよく考えられる」は非常に主観的です。これが独自のアプリであり、プロトタイプを拡張する理由がある場合は、問題はありません。他の人が使用するために配布しているのがライブラリ/モジュール/フレームワークである場合、私はあなたに同意します。しかし、コメントはこれを議論する場所ではありません-問題はコメントで提供されるべきよりも多くの説明と詳細に値します。
感謝します

最初は醜いです:関数として定義されたflatMapはコードを混乱させます-mapグローバル関数として定義された関数型コードを想像してください!2番目は悪い習慣です
Sergey Alaev 2016年

3
@SergeyAlaev re:「マップをグローバル関数として想像してください」ハハ私は多くのユーティリティ関数でまさにそれを行っています。私も矢印関数のシーケンスを使用してそれらをカレーします。「醜い」は主観的です。そして、あなたはあなたの「悪い習慣」のコメントを支持する議論をしていません。あなたの発言は、関数型プログラミングの経験がほとんどないことを私に示しています。プレリュードに「グローバル」の山が含まれているHaskellのような言語を考えてみましょう...誰もそれが「醜い」または「悪い習慣」だと言っているわけではありません。あなたはそれをよく知らないだけです。
感謝します

5
持って@SergeyAlaevかを検討ラケットappendmapfilterfoldlfoldrデフォルトの名前空間の無数の他の中。誰かが「グローバルは悪い」または「ネイティブの拡張は悪い」と言うのを聞いたので、それは悪い習慣にはなりません-ラケットは最高に設計された言語の1つです。あなたはそれを適切に行う方法/時期がわからないだけです。
感謝します

48

flatMapES2019(ES10)の一部としてTC39によって承認されています。次のように使用できます。

[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]

これが私自身のメソッドの実装です:

const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])

flatMapのMDN記事


特に、flapMapは、MDNに係るノード11.0.0に公式である:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
カールウォルシュ

3
@CarlWalsh申し訳ありませんが、抵抗できません。一体何flapMapですか?マップをフラップするのではなく、フラットにしたいのです。
ErikE

別のテーマでは、これは最終的にJavaScript配列をMonads™️に変えます🙃
Kutyel19年

これは古い答えですが、特に大きな配列では非常に非効率的です(新しい配列を作成し、各反復で前のすべての要素をコピーします)。別のアプローチは、concatconst flatMap = (arr, ...args) => [].concat(...arr.map(...args));を使用applyすることです(またはspread演算子を使用できない場合は使用します)-このconcatバージョンには、以下のソリューションで説明されているように、巨大な配列の呼び出しスタックサイズの問題があります
BaliBalo19年

10

自分で定義したくないとおっしゃっていたのは知っていますが、この実装は非常に簡単な定義です。

同じgithubページからもこれがあります:

これは、es6 Spreadを使用する少し短い方法で、renaudtertraisと同様ですが、es6を使用し、プロトタイプに追加しません。

var flatMap = (a, cb) => [].concat(...a.map(cb))

const s = (v) => v.split(',')
const arr = ['cat,dog', 'fish,bird']

flatMap(arr, s)

これらのどちらかが役に立ちますか?

この後者の「ソリューション」は、非常に大きな(たとえば、300k要素)配列で呼び出された場合、「最大呼び出しスタックサイズを超えた」という問題が発生することに注意してください(@ftorのおかげです)a


2
この実装は、大きなアレイのスタックを爆破する可能性があります。

明確にするために、私があなたを正しく理解しているなら、@ ftor、あなたは私が与えたリンクの再帰的実装について話しているのであって、私の答えの最後のコードブロックに示しているものではありませんよね?
Alan Mimms

2
試してみてください[].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase())))。確かに、それはコーナーケースです。しかし、少なくともそれについて言及する必要があります。

ありがとう。この効果にメモを追加しました。
Alan Mimms 2016年

7

Lodashはフラットマップ関数を提供します。これは私にとってはJavascriptがネイティブに提供するのと実質的に同等です。Lodashユーザーでない場合は、ES6のArray.reduce()方法で同じ結果が得られますが、マップしてから個別のステップでフラット化する必要があります。

以下は、各メソッドの例であり、整数のリストをマッピングし、オッズのみを返します。

Lodash:

_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])

ES6リデュース:

[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )

4

かなり簡潔なアプローチの1つは、Array#concat.apply:を利用することです。

const flatMap = (arr, f) => [].concat.apply([], arr.map(f))

console.log(flatMap([1, 2, 3], el => [el, el * el]));


1

私はこのようなことをしました:

Array.prototype.flatMap = function(selector){ 
  return this.reduce((prev, next) => 
    (/*first*/ selector(prev) || /*all after first*/ prev).concat(selector(next))) 
}

[[1,2,3],[4,5,6],[7,8,9]].flatMap(i => i); //[1, 2, 3, 4, 5, 6, 7, 8, 9]

[{subarr:[1,2,3]},{subarr:[4,5,6]},{subarr:[7,8,9]}].flatMap(i => i.subarr); //[1, 2, 3, 4, 5, 6, 7, 8, 9]


こんにちはserhii、私はこのコードを試しました。親が1つのオブジェクトだけで予期しない結果が生じる場合です。`` `Array.prototype.flatMap = function(selector){if(this.length == 1){returnselector(this [0]); } return this.reduce((prev、next)=>(/ * first * / selector(prev)|| / * all after first * / prev).concat(selector(next)))}; `` `
ジョニー

0

これflatMap()でJavascriptができました!そしてそれはかなりよくサポートされています

flatMap()メソッドは、最初にマッピング関数を使用して各要素をマップし、次に結果を新しい配列にフラット化します。これは、map()の後に深さ1のflat()が続くのと同じです。

const dublicate = x => [x, x];

console.log([1, 2, 3].flatMap(dublicate))


投稿のコードの実行時にエラーを取得する{ "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }
SolutionMill

どのブラウザを使用していますか?Edge、IE、Samsung Internetはまだサポートしていませんが、EdgeはまもなくV8で動作します。だから、それらをサポートするために、あなたはpolyfil使用することができます
BIGINT

クロムバージョン67.0.3396.87(公式ビルド)(64ビット)
SolutionMill

お使いのブラウザは古くなっています。ブラウザを更新するか、自動的に更新するように設定する必要があります。
BIGINT

0

Array.prototype.flatMap()JSに到着しました。ただし、すべてのブラウザがそれらをサポートしているわけではなく、MozillaWebドキュメントで現在のブラウザの互換性を確認してください。

flatMap()メソッドは、最初にコールバック関数を引数として使用して各要素をマップし、次に結果を新しい配列にフラット化します(要素がフラット化されるため、2d配列は1dになります)。

関数の使用方法の例もあります。

let arr = [[2], [4], [6], [8]]

let newArr = arr.flatMap(x => [x * 2]);

console.log(newArr);

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