Frage an arrays, javascript, flatten – Ein Array von Arrays in JavaScript zusammenführen / reduzieren?

852

Ich habe ein JavaScript-Array wie:

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

Wie würde ich vorgehen, um die einzelnen inneren Arrays zu einem zusammenzuführen:

["$6", "$12", "$25", ...]
Alle verwendeten Lösungenreduce + concat sind O ((N ^ 2) / 2) wo als angenommene Antwort (nur ein Anruf anconcat) wäre höchstens O (N * 2) in einem schlechten Browser und O (N) in einem guten Browser. Auch Denys Lösung ist für die eigentliche Fragestellung optimiert und bis zu 2x schneller als die Singleconcat. Für diereduce Leute, es macht Spaß, sich cool zu fühlen, wenn man winzigen Code schreibt, aber wenn das Array 1000 Subarrays mit einem Element hätte, würden alle Reduction + Concat-Lösungen funktionieren500500 Operationen wo wie die einzelne concat oder einfache Schleife 1000 Operationen tun würde. gman
Einfach User Spread Operator[].concat(...array) Oleg
gist.github.com/Nishchit14/4c6a7349b3c778f7f97b912629a9f228 Dieser Link beschreibt die Abflachung von ES5 und ES6 Nishchit Dhanani

Deine Antwort

30   die antwort
46

Sie können verwendenUnterstreichen:

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

_.flatten(x); // => [1, 2, 3, 4]
1+ - Sie können auch angeben, dass Sie ein flach abgeflachtes Array wünschendurch Angabetrue für das zweite Argument. Josh Crozier
@ JBRWilkinson Vielleicht möchte JS dir eine Haskell für das Gute lernen !? styfle
Whoah, hat jemand Perl zu JS hinzugefügt? :-) JBRWilkinson
Der Vollständigkeit halber zu @ styfle's Kommentar:learnyouahaskell.com Simon A. Eugster
5

einfach die beste lösung ohne lodash

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
4

Das ist nicht schwer, iterieren Sie einfach über die Arrays und führen Sie sie zusammen:

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

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

Ein Haskellesque Ansatz

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

Warum hat diese Antwort keine höheren Bewertungen? !! monners
Stimmen Sie mit @monners überein. Zweifellos die eleganteste Lösung in diesem ganzen Thread. Nicolás Fantone
29

Eine Lösung für den allgemeineren Fall, dass Ihr Array möglicherweise Elemente enthält, die keine Arrays sind.

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;
}
@ r3wt hat einen Fix hinzugefügt. Was Sie tun müssen, ist sicherzustellen, dass das aktuelle Array, das Sie pflegen,r, verketten die Ergebnisse der Rekursion. aug
Als Methode für Arrays hinzugefügt: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
Dieser Ansatz war sehr effektiv bei der Reduzierung der verschachtelten Array-Form von Ergebnismengen, die Sie von a erhaltenJsonPath-Abfrage. kevinjansz
Diese Antwort ist nicht vollständig! Das Ergebnis der Rekursion wird niemals irgendwo zugewiesen, sodass das Ergebnis der Rekursion verloren geht r3wt
Dies wird unterbrochen, wenn wir das zweite Argument manuell übergeben. Versuchen Sie zum Beispiel Folgendes:flattenArrayOfArrays (arr, 10) oder diesesflattenArrayOfArrays(arr, [1,[3]]); - Diese zweiten Argumente werden zur Ausgabe hinzugefügt. Om Shankar
23

Um ein Array von Einzelelement-Arrays zu reduzieren, müssen Sie keine Bibliothek importieren. Eine einfache Schleife ist sowohl die einfachste als auch die einfachstehöchsteffizient lösung:

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

Downvoter: Bitte lesen Sie die Frage, stimmen Sie nicht ab, weil sie nicht zu Ihrem ganz anderen Problem passt. Diese Lösung ist sowohl die schnellste als auch die einfachste für die gestellte Frage.

Wenn es verfügbar ist, ist es nicht verrückt, aber es macht die Dinge nur weniger lesbar und langsamer ... Denys Séguret
@ Andreas Es sind nicht nur Sie, ich habe versucht herauszufinden, was der Pfeiloperator tut. Wenn Sie rückwärts über ein Array iterieren, riecht es nach einer Mikrooptimierung. millimoose
Diese Schleife ist unnötig kryptisch gotofritz
in der Tat bist du richtig. Ich habe mich zu sehr auf die anderen Beispiele konzentriert - nicht explizit auf den Wortlaut. jonschlinkert
7

Wenn Sie nur Arrays mit einem String-Element haben:

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

werde den Job machen. Übrigens, das genau zu Ihrem Codebeispiel passt.

Wer auch immer abstimmt, bitte erklären Sie warum. Ich habe nach einer anständigen Lösung gesucht und von allen Lösungen hat mir diese am besten gefallen. Anonymous
Ich liebe diese lösung =) Huei Tan
Es behandelt nicht nur Arrays mit 1 String-Elementen, sondern auch dieses Array['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',') alucic
@Anonymous Ich habe es nicht abgelehnt, da es die Anforderungen der Frage technisch erfüllt, aber es ist wahrscheinlich, weil dies eine ziemlich schlechte Lösung ist, die im allgemeinen Fall nicht nützlich ist. Wenn man bedenkt, wie viele bessere Lösungen es hier gibt, würde ich niemals jemandem empfehlen, sich für diese Lösung zu entscheiden, da sie in dem Moment bricht, in dem Sie mehr als ein Element haben oder wenn es sich nicht um Saiten handelt. Thor84no
64

Die meisten Antworten funktionieren hier nicht auf großen Arrays (z. B. 200.000 Elemente), und selbst wenn, sind sie langsam.Antwort von polkovnikov.ph hat die beste Leistung, aber es funktioniert nicht für eine tiefe Abflachung.

Hier istDie schnellste Lösung, die auch auf Arrays mit mehreren Verschachtelungsebenen funktioniert:

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;
};
BeispieleRiesige Arrays
flatten(Array(200000).fill([1]));

Es verarbeitet große Arrays ganz gut. Auf meinem Computer dauert die Ausführung dieses Codes ca. 14 ms.

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

Es funktioniert mit verschachtelten Arrays. Dieser Code erzeugt[1, 1, 1, 1, 1, 1, 1, 1].

Arrays mit verschiedenen Verschachtelungsebenen
flatten([1, [1], [[1]]]);

Es gibt keine Probleme mit dem Reduzieren von Arrays wie diesem.

Bis auf dein riesiges Array ist es ziemlich flach. Diese Lösung funktioniert nicht für tief verschachtelte Arrays. Keine rekursive Lösung wird. Tatsächlich hat derzeit kein Browser außer Safari eine TCO, sodass kein rekursiver Algorithmus eine gute Leistung erbringt. nitely
Was ist die Komplexität der O-Notation? George Katsanos
@ MichałPerłakowski Ich habe das gerade getestetstackoverflow.com/a/49895394/4948383 mit riesigen Array und es funktioniert. Manoj Yadav
@nitely Aber in welcher realen Situation hätten Sie Arrays mit mehr als ein paar Verschachtelungsebenen? Michał Perłakowski
34

Es gibt eine neue native ECMA 2018-Methode namenseben genau das zu tun.

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]]
-1; Nein, das ist nicht Teil vonECMAScript 2018. Es ist immer noch nur ein Vorschlag, der es nicht in eine ECMAScript-Spezifikation geschafft hat. Mark Amery
Ich denke, jetzt können wir darüber nachdenken, denn jetzt ist es Teil des Standards (2019). Können wir den Performance-Teil hier noch einmal wiederholen? Yuvaraj
Es ist eine Schande, dass dies nicht einmal auf der ersten Seite der Antworten steht. Diese Funktion ist in Chrome 69 und Firefox 62 verfügbar (und in Node 11 für diejenigen, die im Backend arbeiten). Matt M.
5

Ich empfehle eine platzsparendeGeneratorfunktion:

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

Falls gewünscht, erstellen Sie ein Array mit abgeflachten Werten wie folgt:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]
Ich mag diesen Ansatz. Ähnlich zustackoverflow.com/a/35073573/1175496 , verwendet aber den Spread-Operator... durch den Generator iterieren. The Red Pea
52

Update: Es stellte sich heraus, dass diese Lösung bei großen Arrays nicht funktioniert. Wenn Sie eine bessere, schnellere Lösung suchen, sehen Sie sich das andiese Antwort.

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

Wird einfach erweitertarr und übergibt es als Argumente anconcat(), das alle Arrays zu einem zusammenfügt. Es ist äquivalent zu[].concat.apply([], arr).

Sie können dies auch zum Abflachen versuchen:

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

Siehe Demo aufJSBin.

Referenzen für ECMAScript 6-Elemente, die in dieser Antwort verwendet werden:

Spread-OperatorPfeilfunktionen

Randnotiz: Methoden wiefind() und Pfeilfunktionen werden nicht von allen Browsern unterstützt. Dies bedeutet jedoch nicht, dass Sie diese Funktionen derzeit nicht verwenden können. Benutz einfachBabel - Es wandelt ES6-Code in ES5 um.

@ LUH3417 Es ist nicht so, ich weiß deine Kommentare wirklich zu schätzen. Es stellte sich heraus, dass Sie Recht haben - diese Lösung funktioniert in der Tat nicht mit großen Arrays. Ich habe geposteteine andere Antwort Das funktioniert auch mit Arrays von 200 000 Elementen. Michał Perłakowski
@GEMI Wenn Sie beispielsweise mit dieser Methode versuchen, ein Array mit 500000 Elementen zu reduzieren, wird "RangeError: Maximale Größe des Aufrufstapels überschritten" angezeigt. Michał Perłakowski
Denn fast alle Antworten hier missbrauchenapply Auf diese Weise habe ich meine Kommentare aus Ihren entfernt. Ich denke immer noch mitapply/ auf diese Weise verbreiten ist schlecht beraten, aber da kümmert sich niemand ... user6445533
Was meinst du mit "funktioniert nicht mit großen Arrays"? Wie groß? Was geschieht? GEMI
Wenn Sie ES6 verwenden, können Sie weiter reduzieren, um:const flatten = arr => [].concat(...arr) Eruant
14

Bitte beachten Sie: WannFunction.prototype.apply ([].concat.apply([], arrays)) oder der Spread-Betreiber ([].concat(...arrays)) wird verwendet, um ein Array zu verflachen. Beides kann bei großen Arrays zu Stapelüberläufen führen, da jedes Argument einer Funktion auf dem Stapel gespeichert wird.

Hier eine stapelsichere Implementierung im funktionalen Stil, die die wichtigsten Anforderungen gegeneinander abwägt:

WiederverwendbarkeitLesbarkeitPrägnanzPerformance

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

Sobald Sie sich an kleine Pfeilfunktionen in Curryform, Funktionszusammensetzung und Funktionen höherer Ordnung gewöhnt haben, liest sich dieser Code wie eine Prosa. Das Programmieren besteht dann lediglich darin, kleine Bausteine ​​zusammenzustellen, die immer wie erwartet funktionieren, da sie keine Nebenwirkungen enthalten.

Wenn Sie feststellen, dass Sie Funktionen von Sprache A in Sprache B implementieren, die nicht Teil des Projekts sind, mit dem einzigen Ziel, genau dies zu tun, dann hat jemand irgendwo eine falsche Wendung eingeschlagen. Könntest du das sein? Ich gehe einfach mitconst flatten = (arr) => arr.reduce((a, b) => a.concat(b), []); spart Ihnen visuellen Müllund Erkläre deinen Teamkollegen, warum du 3 zusätzliche Funktionen benötigstund Einige Funktionsaufrufe auch. Daerdemandt
@ MichałPerłakowski Wenn Sie sie an mehreren Orten verwenden müssen, erfinden Sie das Rad nicht neu und wählen Sie eine Verpackung ausdiese - von anderen Personen dokumentiert und unterstützt. Daerdemandt
Haha. Respektieren Sie Ihre Antwort voll und ganz, auch wenn das Lesen einer solchen funktionalen Programmierung für mich immer noch das Lesen von japanischen Zeichen für Zeichen ist (englischer Sprecher). Tarwin Stroh-Spijer
@Daerdemandt Aber wenn du es als separate Funktionen schreibst, wirst du sie wahrscheinlich in anderem Code wiederverwenden können. Michał Perłakowski
1478

Sie können verwendenconcat Arrays zusammenführen:

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

console.log(merged);

Verwendung derapply Methode vonconcat Nimmt nur den zweiten Parameter als Array, so ist die letzte Zeile identisch mit diesem:

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

Es gibt auch eine experimentelleArray.prototype.flat() Methode (noch nicht Teil des ECMAScript-Standards), mit der Sie die Arrays jedoch reduzieren könnenEs ist noch nicht in Edge oder Node.js verfügbar.

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

Ich verstehe die letzte Änderung von @rofrol nicht. Jetzt macht der zweite Teil der Antwort keinen Sinn -Die letzte Zeile ist also identisch kann nicht wahr sein, da diemerged Variable wäre noch nicht definiert, und Sie würden bekommen‌ReferenceError: merged is not defined. iled
OderArray.prototype.concat.apply([], arrays). danhbear
Beachten Sie, dassconcat ändert nicht das Quell-Array, so dass diemerged Array bleibt nach dem Aufruf von leerconcat. Besser etwas sagen wie:merged = merged.concat.apply(merged, arrays); Nate
wie @Sethi erwähnt, für ES6:[].concat(...arrays)  funktioniert auch in Typescript Eugenio
15
const common = arr.reduce((a, b) => [...a, ...b], [])
24

Was ist mitreduce(callback[, initialValue]) Methode vonJavaScript 1.8

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

Würde den Job machen.

[[1], [2,3]].reduce( (a,b) => a.concat(b), [] ) ist sexy. golopot
Benötigen Sie kein zweites Argument in unserem Fall einfach[[1], [2,3]].reduce( (a,b) => a.concat(b)) Umair Ahmed
3

Ich schlage zwei kurze Lösungen ohne Rekursion vor. Sie sind unter dem Gesichtspunkt der Komplexität der Berechnungen nicht optimal, funktionieren aber im Durchschnitt einwandfrei:

let a = [1, [2, 3], [[4], 5, 6], 7, 8, [9, [[10]]]];

// Solution #1
while (a.find(x => Array.isArray(x)))
    a = a.reduce((x, y) => x.concat(y), []);

// Solution #2
let i = a.findIndex(x => Array.isArray(x));
while (i > -1)
{
    a.splice(i, 1, ...a[i]);
    i = a.findIndex(x => Array.isArray(x));
}
38

Generische Prozeduren bedeuten, dass wir die Komplexität nicht jedes Mal neu schreiben müssen, wenn wir ein bestimmtes Verhalten anwenden müssen.

concatMap (oderflatMap) ist genau das, was wir in dieser Situation brauchen.

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

Voraussicht

Und ja, Sie haben es richtig geraten, es flacht nur abein eben, das ist genau so wie es istsollte Arbeit

Stellen Sie sich einen Datensatz wie diesen vor

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

Ok, jetzt sagen wir, wir wollen eine Liste drucken, in der alle Spieler aufgeführt sind, die teilnehmen werdengame

const gamePlayers = game =>
  flatten (game)

gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]

Falls unserflatten Prozedur reduziert verschachtelte Arrays auch, wir würden mit diesem Müll Ergebnis enden ...

const gamePlayers = game =>
  badGenericFlatten(game)

gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]
rollt tief, Baby

Das soll nicht heißen, dass Sie manchmal auch verschachtelte Arrays nicht reduzieren möchten - nur sollte dies nicht das Standardverhalten sein.

Wir können eine machendeepFlatten Verfahren mit Leichtigkeit ...

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

Dort. Jetzt haben Sie ein Werkzeug für jeden Job - eines zum Quetschen einer Verschachtelungsebene.flattenund eine zum Auslöschen aller VerschachtelungendeepFlatten.

Vielleicht kannst du es nennenobliterate odernuke wenn dir der name nicht gefälltdeepFlatten.

Wiederholen Sie nicht zweimal!

Natürlich sind die obigen Implementierungen clever und prägnant, aber unter Verwendung von a.map gefolgt von einem Anruf bei.reduce Das heißt, wir machen tatsächlich mehr Iterationen als nötig

Mit einem vertrauenswürdigen Kombinator rufe ich anmapReduce hilft dabei, die Iterationen auf ein Minimum zu beschränken; Es nimmt eine Zuordnungsfunktionm :: a -> beine reduzierende Funktionr :: (b,a) ->b und gibt eine neue Reduzierungsfunktion zurück - dieser Kombinator ist das Herzstück vonWandler; wenn Sie interessiert sind,Ich habe andere Antworten darüber geschrieben

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

Wenn ich Ihre Antworten sehe, möchte ich meine häufig zurückziehen, weil sie wertlos geworden sind. Gute Antwort!concat selbst sprengt nicht den Stapel, nur... undapply tut (zusammen mit sehr großen Arrays). Ich habe es nicht gesehen. Ich fühle mich gerade schrecklich. user6445533
Ich denke ein besserer Name wäreflatMap, denn genau das ist wasconcatMap ist derbind Instanz derlist Monade.concatpMap ist implementiert alsfoldr ((++) . f) []. Übersetzt in Javascript:const flatMap = f => foldr(comp(concat) (f)) ([]). Dies ist natürlich ähnlich wie bei Ihrer Implementierung ohnecomp. user6445533
stimme dem für Haskell zu WeiChing Lin
Was ist die Komplexität dieses Algorithmus? George Katsanos
6

Ich würde lieber das gesamte Array, wie es ist, in eine Zeichenfolge umwandeln, aber im Gegensatz zu anderen Antworten würde ich dies mithilfe von tunJSON.stringify und nicht dietoString() Methode, die ein unerwünschtes Ergebnis erzeugen.

DamitJSON.stringify Alles, was übrig bleibt, ist, alle Klammern zu entfernen, das Ergebnis noch einmal in Start- und Endklammern zu setzen und das Ergebnis mit zu liefernJSON.parse was die Saite wieder zum "Leben" erweckt.

Kann unendlich verschachtelte Arrays ohne Geschwindigkeitsaufwendungen verarbeiten.Kann mit Array-Elementen umgehen, die Zeichenfolgen sind, die Kommas enthalten.
var arr = ["abc",[[[6]]],["3,4"],"2"];

var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);

console.log(flattened)
Nur für mehrdimensionales Array von Strings / Numbers (keine Objekte)
@GeorgeKatsanos - Diese Methode funktioniert nicht für Array-Elemente (und verschachtelte Elemente), die keinen primitiven Wert haben, z. B. ein Element, das auf ein DOM-Element verweist vsync
@vsync - Ich habe es verstanden. gkd
Ich habe falsch verstanden pizza-r0b
das scheint definitiv die schnellste Lösung zu sein, die ich dafür gesehen habe; Sind Ihnen Fallstricke in @vsync bekannt? (Abgesehen von der Tatsache, dass es natürlich ein bisschen kitschig aussieht - verschachtelte Arrays als Zeichenfolgen behandeln: D) George Katsanos
5

Ich habe es mit Rekursion und Closures gemacht

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;
}
AFAIK ist lexikalisch und kein Abschluss dashambles
Einfach und süß, diese Antwort funktioniert besser als die akzeptierte Antwort. Es glättet tief verschachtelte Ebenen auf, nicht nur die erste Ebene Om Shankar
@dashambles ist korrekt - der Unterschied besteht darin, dass Sie, wenn es sich um eine Schließung handelt, die innere Funktion nach außen zurückgeben und nach Abschluss der äußeren Funktion die innere Funktion weiterhin verwenden können, um auf ihren Gültigkeitsbereich zuzugreifen. Hier ist die Lebensdauer der äußeren Funktion länger als die der inneren Funktion, so dass niemals ein "Verschluss" erzeugt wird. Mörre
415

Hier ist eine kurze Funktion, die einige der neueren JavaScript-Array-Methoden verwendet, um ein n-dimensionales Array zu reduzieren.

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

Verwendungszweck:

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]
Ich mag diesen Ansatz. Es ist viel allgemeiner und unterstützt verschachtelte Arrays alfredocambera
Es ist eine schöne Lösung, und in den meisten Fällen würde ich sie gerne verwenden, aber die Leistung ist möglicherweise nicht die beste: Wenn Sie für eine Array-Struktur, die auf untergeordneter Ebene sehr viele Werte aufweist, viele Werte haben, werden diese trotzdem durchlaufen es ändert sie nicht, also z Im Beispiel mit den Fragen werden alle Arrays durchlaufen, nicht nur das übergeordnete Array. Auch wenn es nichts anderes als die erste Ebene abflachen muss. Ich hatte (natürlich) Leistungsprobleme mit der Array-Struktur von über 10000 Arrays innerhalb von 2 übergeordneten Arrays. Hachi
Angesichts der mehrstufigen geschachtelten Unterstützung vonreduceDies ist viel performanter als die heute akzeptierte Antwort Saba Ahang
Warum wird ein leeres Array als Argument übergeben? Der Code bricht ohne es, aber was macht es? ayjay
7
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

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

(Ich schreibe dies nur als separate Antwort, basierend auf dem Kommentar von @danhbear.)

9
ES6 Einzeilige Abflachung

Sehenlodash Abflachen, unterstreichen abflachentrue)

function flatten(arr) {
  return arr.reduce((acc, e) => acc.concat(e), []);
}

oder

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

Getestet mit

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

Sehenlodash flattenDeep, unterstreichen abflachen

function flattenDeep(arr) {
  return arr.reduce((acc, e) => Array.isArray(e) ? acc.concat(flattenDeep(e)) : acc.concat(e), []);
}

Getestet mit

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]);
});
Dein 2. Beispiel ist besser geschrieben alsArray.prototype.concat.apply([], arr) weil Sie ein zusätzliches Array erstellen, nur um an die zu gelangenconcat Funktion. Laufzeiten können es optimieren oder nicht, wenn sie es ausführen, aber der Zugriff auf die Funktion auf dem Prototyp sieht nicht hässlicher aus, als dies ohnehin schon der Fall ist. Mörre
7

ES6 Weg:

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

ES5 Weg fürflatten Funktion mit ES3-Fallback für N-fach verschachtelte Arrays:

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

ES6 funktioniert übrigens bei mir. Axel
5

Ich habe mit vermasseltES6 Generatoren neulich und schriebdieser Kern. Was beinhaltet...

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

Grundsätzlich erstelle ich einen Generator, der das ursprüngliche Eingabearray durchläuft, wenn er ein Array findet, das er verwendetAusbeute* Operator in Kombination mit Rekursion, um die internen Arrays kontinuierlich zu reduzieren. Wenn das Element kein Array ist, ist es nurAusbeuten der einzelne Artikel. Dann mit demES6-Spread-Operator (aka splat operator) Ich drücke den Generator in eine neue Array-Instanz.

Ich habe die Leistung nicht getestet, aber ich denke, es ist ein schönes einfaches Beispiel für die Verwendung von Generatoren und dem Yield * -Operator.

Aber auch hier habe ich nur gepatzt, also bin ich sicher, dass es leistungsfähigere Möglichkeiten gibt, dies zu tun.

Ähnliche Antwort (mit Generator und Ertragsdelegation), aber inPHP-Format The Red Pea
3

Heutzutage ist der beste und einfachste Weg dies zu tun, das Array so zusammenzufügen und aufzuteilen.

var multipleArrays = [["$6","$Demo"], ["$12",["Multi","Deep"]], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]]

var flattened = multipleArrays.join().split(",")

Diese Lösung arbeitet mit mehreren Ebenen und ist auch oneliner.

DEMO

EDIT für ECMAScript 6

Da ECMAScript 6 standardisiert wurde, können Sie die Operation ändern[].concat.apply([], arrays); zum[].concat(...arrays);

var flattened = [].concat(...input);

DEMO

EDIT Effizienteste Lösung

Der effizienteste Weg, um das Problem zu lösen, ist die Verwendung einer Schleife. Sie können die Geschwindigkeit "ops / sec" vergleichenHier

var flattened=[];
for (var i=0; i<input.length; ++i) {
    var current = input[i];
    for (var j=0; j<current.length; ++j)
        flattened.push(current[j]);
} 

DEMO

Ich hoffe es hilft

Es funktioniert nicht, wenn das Array Zeichenfolgen enthält, die Kommas enthalten, zum Beispiel:[[","]].join().split(",") gibt nicht das gewünschte Ergebnis. Michał Perłakowski
282

Hier ist eine einfache und performante funktionale Lösung:

var result = [].concat.apply([], [[1],[2,3],[4]]);
console.log(result); // [ 1, 2, 3, 4 ]

Kein zwingendes Durcheinander.

funktionierte nicht mit Array [2, [3, [4, [5, [6, [7, [8]]]]] pareshm
Wenn Sie ES2015 verwenden können, können Sie es auch mit Array-Spreads einfacher für das Auge schreiben:[].concat(...[ [1],[2,3],[4] ]). Loilo
@paldepind Der vorgeschlagene Ansatz verwendet keine Bibliothek, sondern die Standard-JS-Funktionen. Es mutiert keine Werte, deshalb ist es nicht zwingend, sondern funktional. Also verstehe ich nicht, was genau du dumm fandest. Nikita Volkov
1. Kein Problem da bin ich schon. Sie haben Recht, dass Ihre Lösung in diesem Sinne funktionsfähig ist. Ich glaube, ich habe Ihren ursprünglichen Punkt falsch verstanden. 2. Eine Bibliothek ist immer noch eine Bibliothek, obwohl sie in C implementiert ist. Ein großer Teil der Pythons-Standardbibliothek ist auch in C implementiert. Das sind nur Implementierungsdetails der Bibliothek und sie sind immer noch genauso viele Bibliotheken. 3. Richtig. 4. Nein, das sage ich nicht. paldepind
163

Es kann am besten durch Javascript Reduce-Funktion erfolgen.

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

Oder mit ES2015:

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

js-Geige

Mozilla-Dokumente

@calbertts Übergebe einfach den Anfangswert[] Weitere Validierungen sind nicht erforderlich. user6445533
@JohnS Reduziert Feeds tatsächlich auf einen einzelnen (Array-) Wert pro Runde. Beweis? Nehmen Sie redu () heraus und prüfen Sie, ob es funktioniert. reduce () ist hier eine Alternative zu Function.prototype.apply () André Werlang
In TypeScript:var array = arrays.reduce((a, b) => a.concat(b)); Vojta
Ich hätte auch Reduce verwendet, aber eine interessante Sache, die ich aus diesem Snippet gelernt habe, ist die folgendeMeistens müssen Sie keinen initialValue übergeben :) José F. Romaniello
22

Eine weitere ECMAScript 6-Lösung im funktionalen Stil:

Deklarieren Sie Funktion:

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

und benutze es:

flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6]

AKTUALISIEREN:

Betrachten Sie auch eine native FunktionArray.prototype.flat () (Vorschlag für ES6) ist in den letzten Versionen moderner Browser verfügbar.

Dank an @ (Константин Ван) und @ (Mark Amery) hat es in den Kommentaren erwähnt.

-1;flat ist nicht Teil vonES6. Es ist nicht einmal ein Teil vonES9. Es ist einVorschlag, noch nicht Teil der ECMAScript-Spezifikation, undnoch nicht in allen gängigen JavaScript-Umgebungen verfügbar. Mark Amery
Das ist schön und ordentlich, aber ich denke, Sie haben eine ES6-Überdosis gemacht. Es ist nicht erforderlich, dass die äußere Funktion eine Pfeilfunktion ist. Ich würde bei der Pfeil-Funktion für den Rückruf reduzieren bleiben, aber Abflachen selbst sollte eine normale Funktion sein. Stephen Simpson
Bekommen einRangeError: Maximum call stack size exceeded Matt Westlake
@StephenSimpson, aber muss die äußere Funktion a sein?nicht-Pfeil-Funktion?"Abflachen selbst sollte eine normale Funktion sein" - Mit "normal" meinen Sie "Nichtpfeil", aber warum?Warum benutze eine pfeil funktion im aufruf um dann zu reduzieren? Können Sie uns Ihre Argumentation mitteilen? user633183
5

Es sieht so aus, als wäre dies ein Job für RECURSION!

Verarbeitet mehrere VerschachtelungsebenenVerarbeitet leere Arrays und Nicht-Array-ParameterHat keine MutationVerlässt sich nicht auf moderne Browserfunktionen

Code:

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

Verwendungszweck:

flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7] 
@pietrovismara, mein Test ist ungefähr 1s. Ivan Yan
Vorsichtig,flatten(new Array(15000).fill([1])) wirftUncaught RangeError: Maximum call stack size exceeded und habe meine devTools für 10 Sekunden eingefroren pietrovismara
4

das Eingabe-Array in einen String umzuwandeln und alle Klammern ([]) zu entfernen und die Ausgabe in ein Array zu analysieren. Ich verwende dafür die ES6-Vorlagenfunktion.

var x=[1, 2, [3, 4, [5, 6,[7], 9],12, [12, 14]]];

var y=JSON.parse(`[${JSON.stringify(x).replace(/\[|]/g,'')}]`);

console.log(y)
Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen. Nur-Code-Antworten leisten nur sehr wenig, um zukünftige SO-Leser zu schulen. Ihre Antwort steht in der Moderationswarteschlange, weil Sie eine schlechte Qualität haben. mickmackusa
Du bist ein Gott, hahaha, die beste Antwort aller Zeiten Waldemar Neto
Dies ist der schnellste und cleverste Weg, dies zu tun. Ich habe dies verwendet, um eine Rekursion zu vermeiden und das Array einfach neu zu erstellen, aber deins ist 3% schneller. Weit weg :)const flatten = function (A) { return A .toString() .split(',') .reduce( (a,c) => { let i = parseFloat(c); c = (!Number.isNaN(i)) ? i : c; a.push(c); return a; }, []); Ady Ngom

Verwandte Fragen