Warum kein Array.prototype.flatMap in Javascript?

Lesezeit: 7 Minuten

flatMap ist unglaublich nützlich für Sammlungen, aber Javascript stellt keine zur Verfügung, während es vorhanden ist Array.prototype.map. Wieso den?

Gibt es eine Möglichkeit zu emulieren flatMap in Javascript auf einfache und effiziente Weise ohne Definition flatMap manuell?

  • “Warum?” Weil noch niemand einen Vorschlag gemacht hat? So schlagen Sie eine neue Funktion vor. “Gibt es eine Möglichkeit, flatMap zu emulieren … ohne flatMap manuell zu definieren?” Äh? Ich verstehe nicht. Du meinst wie arr.reduce((arr, v) => (arr.push(...v), arr), [])?

    – Felix Klinge

    3. Oktober 2016 um 18:04 Uhr


  • (^ Das ist nur der abgeflachte Teil, also denke ich, dass es so sein sollte arr.map(...).reduce(...)).

    – Felix Klinge

    3. Oktober 2016 um 18:10 Uhr


  • Sie könnten das Array danach einfach abflachen .mappingen.

    – kennytm

    3. Oktober 2016 um 18:10 Uhr

  • Hmm, Sie möchten es “emulieren”, aber nicht “definieren”. Was könnte das bedeuten?

    Benutzer663031

    3. Oktober 2016 um 19:00 Uhr

  • Es wird bald Teil von JS sein. tc39.github.io/proposal-flatMap

    – Vijaiendran

    1. November 2017 um 20:57 Uhr

Benutzer-Avatar
Mulan

Aktualisieren: Array.prototype.flatMap hat es in ES2019 geschafft

Es wird in vielen Umgebungen weitgehend unterstützt. Sehen Sie mit diesem Snippet unten, ob es in Ihrem Browser funktioniert –

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 ]

“Warum keine Array.prototype.flatMap in Javascript?”

Weil Programmieren keine Zauberei ist und jede Sprache keine Merkmale/Primitiven hat, die jede andere Sprache hat. Was zählt, ist, dass JavaScript Ihnen die Möglichkeit gibt, es selbst zu definieren –

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

Oder eine Umschreibung, die die beiden Schleifen zu einer zusammenfasst –

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

Wenn Sie es verlängern möchten Array.prototypenichts hält dich auf –

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','♧']
// ]

  • Es wird als besser angesehen, alle Zusätze zu Prototypen zu benennen (z Array.prototype._mylib_flatMap) oder noch besser ES6-Symbole verwenden, anstatt sie einfach zu definieren Array.prototype.flatMap.

    – Fengyang Wang

    4. Oktober 2016 um 2:32 Uhr


  • @FengyangWang „für besser gehalten“ ist höchst subjektiv. Wenn dies Ihre eigene App ist und Sie einen Grund haben, Ihre Prototypen zu erweitern, spricht nichts dagegen. Wenn es sich um eine Bibliothek/ein Modul/ein Framework handelt, das Sie zur Verwendung durch andere verteilen, dann würde ich Ihnen zustimmen. Kommentare sind jedoch nicht der Ort, um dies zu diskutieren – die Angelegenheit verdient mehr Erläuterungen und Details, als in Kommentaren bereitgestellt werden sollten.

    – Mulan

    4. Oktober 2016 um 7:05 Uhr


  • @SergeyAlaev zu: “Stellen Sie sich die Karte als globale Funktion vor” haha Ich mache genau das bei vielen meiner Utility-Funktionen. Ich Curry sie auch mit Sequenzen von Pfeilfunktionen. „Hässlich“ ist subjektiv. Und Sie haben kein Argument vorgebracht, um Ihren Kommentar zu “schlechter Praxis” zu stützen. Ihre Bemerkungen deuten darauf hin, dass Sie wenig Erfahrung mit funktionaler Programmierung haben. Nehmen Sie eine Sprache wie Haskell, wo der Auftakt haufenweise “Globals” enthält … niemand sagt, dass es “hässlich” oder eine “schlechte Praxis” ist. Du kennst dich damit einfach nicht aus.

    – Mulan

    4. Oktober 2016 um 7:34 Uhr


  • @SergeyAlaev oder betrachten Sie Racket, das hat append, map, filter, foldl, foldr unter unzähligen anderen im Standard-Namespace. Es macht es nicht zu einer schlechten Praxis, weil Sie jemanden sagen hören: „Globals sind schlecht“ oder „Eingeborene erweitern ist schlecht“ – Racket gehört zu den am besten gestalteten Sprachen. Du weißt einfach nicht, wie/wann du es richtig machen sollst.

    – Mulan

    4. Oktober 2016 um 7:47 Uhr

  • @user633183 “”für besser gehalten“ist höchst subjektiv.” Nein, ist es nicht. Es ist bedingt. Subjektiv ist kein Synonym für Bedingung. Wie auch immer, Sie skizzieren diese Bedingungen in Ihrer ersten Antwort: „Wenn es Ihre eigene App ist […] daran ist nichts auszusetzen.” “Wenn es sich um eine Bibliothek/ein Modul/ein Framework handelt, das Sie zur Verwendung durch andere verteilen, dann würde ich dir zustimmen.“Ich denke, diese Informationen sollten im Hauptteil der Antwort stehen, da sie irgendwie wichtig sind?

    – Elliot Jones

    2. August 2019 um 8:30 Uhr


Benutzer-Avatar
Kutyel

flatMap wurde vom TC39 im Rahmen von ES2019 (ES10) genehmigt. Sie können es wie folgt verwenden:

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

Hier ist meine eigene Implementierung der Methode:

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

MDN-Artikel auf flatMap

  • Insbesondere ist flapMap laut MDN offiziell in Node 11.0.0: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

    – Karl Walsch

    20. Februar 2019 um 6:39 Uhr

  • @CarlWalsh Es tut mir leid, ich kann nicht widerstehen. Was zum Teufel ist flapMap? Ich möchte meine Karte glätten, nicht flattern!

    – ErikE

    19. März 2019 um 1:48 Uhr

  • Zu einem anderen Thema, dies verwandelt schließlich JavaScript Arrays in Monads™️ 🙃

    – Kutyel

    29. März 2019 um 17:54 Uhr

Benutzer-Avatar
Alan Mimms

Ich weiß, du hast gesagt, du wolltest es nicht selbst definieren, aber diese Umsetzung ist eine ziemlich triviale Definition.

Es gibt auch dies von derselben Github-Seite:

Hier ist ein etwas kürzerer Weg mit der Verbreitung von es6, ähnlich wie bei Renaudtertrais – aber mit der Verwendung von es6 und ohne Hinzufügen zum Prototyp.

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

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

flatMap(arr, s)

Würde eines davon helfen?

Es sollte beachtet werden (dank @ftor), dass diese letztere “Lösung” unter “Maximale Aufrufstapelgröße überschritten” leidet, wenn sie für ein wirklich großes Array (z. B. 300.000 Elemente) aufgerufen wird a.

  • Diese Implementierung kann den Stapel für große Arrays sprengen.

    Benutzer6445533

    4. Oktober 2016 um 7:08 Uhr

  • Nur um klar zu sein, wenn ich Sie richtig verstehe, @ftor, sprechen Sie über die rekursive Implementierung in dem Link, den ich gegeben habe, nicht über die, die ich im Codeblock am Ende meiner Antwort zeige, oder?

    – Alan Mimms

    4. Oktober 2016 um 22:28 Uhr

  • Versuchen [].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase()))). Sicher, es ist ein Eckfall. Aber man sollte es zumindest erwähnen.

    Benutzer6445533

    5. Oktober 2016 um 7:11 Uhr

  • Vielen Dank. Dazu habe ich eine Anmerkung hinzugefügt.

    – Alan Mimms

    6. Oktober 2016 um 15:43 Uhr

Lodash bietet eine Flatmap-Funktion, die für mich praktisch gleichbedeutend mit Javascript ist, das sie nativ bereitstellt. Wenn Sie kein Lodash-Benutzer sind, dann ES6 Array.reduce() -Methode kann Ihnen das gleiche Ergebnis liefern, aber Sie müssen in diskreten Schritten map-then-flatten.

Nachfolgend finden Sie ein Beispiel für jede Methode, die eine Liste von Ganzzahlen abbildet und nur die Quoten zurückgibt.

Lodash:

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

ES6 Reduzieren:

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

Ein ziemlich prägnanter Ansatz ist die Verwendung von Array#concat.apply:

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

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

Ich habe so etwas gemacht:

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]

Benutzer-Avatar
bigInt

Wir haben jetzt eine flatMap() in Javascript! Und es wird unterstützt ziemlich gut

Die Methode flatMap() ordnet zuerst jedes Element mit einer Mapping-Funktion zu und glättet dann das Ergebnis in einem neuen Array. Es ist identisch mit einem map() gefolgt von einem flat() der Tiefe 1

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

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

  • Fehler beim Ausführen des geposteten Codes { "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }

    – SolutionMill

    13. Juni 2019 um 18:23 Uhr

  • Welchen Browser verwendest du? Edge, IE und Samsung Internet unterstützen es noch nicht, aber Edge wird bald auf V8 laufen. Um diese zu unterstützen, könnten Sie also ein Polyfil verwenden

    – bigInt

    13. Juni 2019 um 18:25 Uhr


  • Chrome-Version 67.0.3396.87 (offizieller Build) (64-Bit)

    – SolutionMill

    13. Juni 2019 um 18:26 Uhr

  • Ihr Browser ist veraltet. Sie sollten ihn aktualisieren oder so einstellen, dass er sich automatisch aktualisiert.

    – bigInt

    13. Juni 2019 um 18:28 Uhr

1175220cookie-checkWarum kein Array.prototype.flatMap in Javascript?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy