Pytanie w sprawie javascript, flatten, arrays – Połącz / spłaszcz tablicę tablic w JavaScript?

852

Mam tablicę JavaScript taką jak:

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

W jaki sposób chciałbym połączyć oddzielne wewnętrzne tablice w jedną:

["$6", "$12", "$25", ...]
gist.github.com/Nishchit14/4c6a7349b3c778f7f97b912629a9f228 Ten link opisuje spłaszczanie ES5 i ES6 Nishchit Dhanani
Po prostu operator rozprzestrzeniania użytkownika[].concat(...array) Oleg
Wszystkie rozwiązania, które wykorzystująreduce + concat są O ((N ^ 2) / 2) gdzie jako zaakceptowana odpowiedź (tylko jedno połączenie doconcat) byłby co najwyżej O (N * 2) w złej przeglądarce, a O (N) w dobrej. Również rozwiązanie Denysa jest zoptymalizowane do aktualnego pytania i do 2x szybsze niż pojedynczeconcat. Dlareduce fajnie jest czuć się fajnie pisząc maleńki kod, ale na przykład, jeśli tablica zawiera 1000 podelementów jednego elementu, wszystkie rozwiązania redukujące + konkatowe będą działać500500 operacji gdzie pojedyncza konkat lub prosta pętla wykonałaby 1000 operacji. gman

Twoja odpowiedź

30   odpowiedzi
38

Ogólne procedury oznaczają, że nie musimy przepisywać złożoności za każdym razem, gdy musimy użyć określonego zachowania.

concatMap (lubflatMap) jest dokładnie tym, czego potrzebujemy w tej sytuacji.

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

dalekowzroczność

I tak, odgadłeś to poprawnie, tylko się spłaszczajeden poziom, czyli dokładnie takpowinien praca

Wyobraź sobie taki zestaw danych

// 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, teraz powiedzmy, że chcemy wydrukować listę, która pokazuje wszystkich graczy, którzy będą uczestniczyćgame

const gamePlayers = game =>
  flatten (game)

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

Jeśli naszflatten procedura spłaszczyła również zagnieżdżone tablice, skończyłybyśmy się tym wynikiem śmieci ...

const gamePlayers = game =>
  badGenericFlatten(game)

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

Nie znaczy to, że czasami nie chcesz także spłaszczać zagnieżdżonych tablic - tylko to nie powinno być domyślnym zachowaniem.

Możemy zrobićdeepFlatten procedura z łatwością…

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

Tam. Teraz masz narzędzie do każdego zadania - jedno do zgniatania jednego poziomu zagnieżdżania,flatteni jeden do zatarcia wszystkich gniazddeepFlatten.

Może możesz to nazwaćobliterate lubnuke jeśli nie podoba ci się nazwadeepFlatten.

Nie powtarzaj dwukrotnie!

Oczywiście powyższe implementacje są sprytne i zwięzłe, ale przy użyciu.map po czym następuje wezwanie do.reduce oznacza, że ​​faktycznie wykonujemy więcej iteracji niż jest to konieczne

Dzwonię za pomocą sprawdzonego łącznikamapReduce pomaga utrzymywać iteracje w minium; przyjmuje funkcję mapowaniam :: a -> b, funkcja redukcjir :: (b,a) ->b i zwraca nową funkcję redukcji - ten kombinator jest sercemprzetworniki; Jeśli jesteś zainteresowany,Napisałem o nich inne odpowiedzi

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

Proszę to zanotowaćconcat w Javascript ma inne znaczenie niż w Haskell. Haskellaconcat ([[a]] -> [a]) zostanie wywołanyflatten w Javascript i jest zaimplementowany jakofoldr (++) [] (Javascript:foldr(concat) ([]) przyjmując funkcje curried). Javascriptconcat to dziwny dodatek ((++) w Haskell), który obsługuje oba[a] -> [a] -> [a] ia -> [a] -> [a]. user6445533
@ LUH3417 nie powinieneś czuć się okropnie. Zapewniasz również dobre odpowiedzi z dobrze napisanymi wyjaśnieniami. Wszystko tutaj jest doświadczeniem - często robię błędy i piszę złe odpowiedzi. Powracam do nich za kilka miesięcy i myślę, że „myślę, że wtf?” i skończyć całkowicie rewidując rzeczy. Żadna odpowiedź nie jest idealna. Wszyscy jesteśmy w pewnym momencie na krzywej uczenia się ^ _ ^ user633183
wybierz to dla Haskella WeiChing Lin
Myślę, że lepiej byłoflatMap, ponieważ to jest dokładnie to, coconcatMap jestbind wystąpienielist monad.concatpMap jest zaimplementowany jakofoldr ((++) . f) []. Przetłumaczone na język Javascript:const flatMap = f => foldr(comp(concat) (f)) ([]). Jest to oczywiście podobne do twojej implementacji bezcomp. user6445533
6

Wolałbym przekształcić całą tablicę, tak jak jest, na ciąg, ale w przeciwieństwie do innych odpowiedzi, zrobiłby to używającJSON.stringify i nie używajtoString() metoda, która daje niepożądany wynik.

Z tymJSON.stringify wyjście, wszystko, co zostało, to usunąć wszystkie nawiasy, ponownie zawinąć wynik nawiasami początkowymi i końcowymi i podać wynik za pomocąJSON.parse który przywraca łańcuch do „życia”.

Może obsługiwać nieskończone zagnieżdżone tablice bez żadnych kosztów związanych z prędkością.Może poprawnie obsługiwać elementy tablicy, które są łańcuchami zawierającymi przecinki.
var arr = ["abc",[[[6]]],["3,4"],"2"];

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

console.log(flattened)
Tylko dla wielowymiarowej tablicy łańcuchów / liczb (nie obiektów)
@GeorgeKatsanos - Ta metoda nie będzie działać dla elementów tablicy (i elementów zagnieżdżonych), które nie mają wartości Primitive, na przykład elementu, który wskazuje element DOM vsync
@vsync - mam to. gkd
@realseanp - źle zrozumiałeś wartość w tym elemencie tablicy. Celowo umieściłem ten przecinek jako wartość, a nie jako przecinek ogranicznika Array, aby podkreślić moc mojego rozwiązania ponad wszystkimi innymi, które mogłyby wygenerować"3,4". vsync
Źle zrozumiałem pizza-r0b
3

Proponuję dwa krótkie rozwiązania bez rekursji. Nie są optymalne z punktu widzenia złożoności obliczeniowej, ale działają dobrze w przeciętnych przypadkach:

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));
}
15
const common = arr.reduce((a, b) => [...a, ...b], [])
7
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

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

(Piszę to jako osobną odpowiedź na podstawie komentarza @danhbear.)

415

Oto krótka funkcja, która wykorzystuje niektóre nowsze metody tablicy JavaScript do spłaszczenia tablicy n-wymiarowej.

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

Stosowanie:

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]
Jest to ładne rozwiązanie iw większości przypadków chętnie z niego korzystam, ale wydajność może nie być najlepsza: jeśli masz strukturę tablicową, która ma najgłębszy poziom podrzędny, wiele wartości, nadal będzie przechodzić przez te, nawet jeśli to ich nie zmienia, np w przykładzie pytania przejdzie przez wszystkie tablice, a nie tylko tablicę nadrzędną. Nawet jeśli nie ma potrzeby spłaszczania niczego innego z wyjątkiem pierwszego poziomu. Miałem (naturalnie) problemy z wydajnością ze strukturą tablicową 10000+ tablic wewnątrz 2 macierzy nadrzędnych. Hachi
@GeorgeKatsanos Nie wiemy tego - tak,concat ma utworzyć nową tablicę, ale środowiska wykonawcze mogą optymalizować tak, jak już to robią, np. dla wielu rodzajów operacji łańcuchowych. Jeśli wykryją, tablica jest natychmiast odrzucana i mogą nawet ograniczyć wykrywanie doreduce działają konkretnie, mogą ponownie używać tej samej tablicy wewnętrznie. Na wasze wywiady powiedziałbym, że każdy, kto uważa, że ​​obserwowalne zachowanie kodu JS zawsze jest dokładnie taki sam, jak to, co robi wewnętrznie, nie może być odrzucony, ale zdecydowanie wykształcony - te rzeczy mogą się zmienić w każdej chwili. Mörre
Dlaczego przekazywana jest pusta tablica jako argument? Kod łamie się bez niego, ale co on robi? ayjay
Biorąc pod uwagę wielopoziomowe zagnieżdżone wsparciereduce, jest to o wiele bardziej skuteczne niż akceptowana odpowiedź na dzisiejsze czasy Saba Ahang
5

Wygląda na to, że to jest zadanie do RECURSION!

Obsługuje wiele poziomów zagnieżdżaniaObsługuje puste tablice i parametry bez tablicyNie ma mutacjiNie polega na nowoczesnych funkcjach przeglądarki

Kod:

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

Stosowanie:

flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7] 
ostrożny,flatten(new Array(15000).fill([1])) rzucaUncaught RangeError: Maximum call stack size exceeded i zamroziłem moje narzędzia na 10 sekund pietrovismara
@pietrovismara, mój test to około 1s. Ivan Yan
282

, Oto proste i wydajne rozwiązanie funkcjonalne:

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

Bez imperatywnego bałaganu.

Ahh, znalazłem twój błąd. W notacji masz dodatkową parę nawiasów kwadratowych[].concat([1],[2,3],[4],...). Ryan Kennedy
W CoffeeScript tak jest[].concat([[1],[2,3],[4]]...) amoebe
1. Nie ma problemu, już jestem. Masz rację, że w tym sensie twoje rozwiązanie jest funkcjonalne. Myślę, że źle zrozumiałem twój pierwotny punkt. 2. Biblioteka nadal jest biblioteką, mimo że jest zaimplementowana w C. Ogromna część standardowej biblioteki Pythona jest również zaimplementowana w C. To tylko szczegóły implementacji biblioteki i nadal są one tak samo dużo bibliotek. 3. Prawda. 4. Nie, to nie to, co mówię. paldepind
Tak, używasz standardowej funkcji biblioteki JavaScript. Ale nie czyni to twojego kodu bardziej funkcjonalnym w sensie „programowania funkcjonalnego”. Rzeczywistą implementacją funkcji może być tzw. „Imperatywny bałagan”. Jest ukryty w bibliotece. Java ma dość rozbudowaną bibliotekę standardową - ale jej użycie nie czyni kodu Java mniej imperatywnym ani bardziej funkcjonalnym. Dlatego uważam twoje oświadczenie za głupie. paldepind
4

To nie jest trudne, po prostu iteruj po tablicach i scal je:

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

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

Sposób 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))

Sposób ES5 dlaflatten funkcja z awarią ES3 dla N-razy zagnieżdżonych tablic:

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

Sposób ES6 działa dla mnie. Axel
5

Polecam oszczędność miejscafunkcja generatora:

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

W razie potrzeby utwórz tablicę spłaszczonych wartości w następujący sposób:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]
Podoba mi się to podejście. Podobny dostackoverflow.com/a/35073573/1175496 , ale używa operatora rozprzestrzeniania... iterować przez generator. The Red Pea
1478

Możesz użyćconcat scalać tablice:

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

console.log(merged);

Używającapply metodaconcat po prostu weźmie drugi parametr jako tablicę, więc ostatnia linia jest identyczna:

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

Jest też eksperymentalnyArray.prototype.flat() metoda (jeszcze nie część standardu ECMAScript), której można użyć do spłaszczenia tablic, chociażnie jest jeszcze dostępny w Edge ani Node.js.

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

Opierając się na komentarzu @ Sethi:Array.prototype.concat(...arrays). Ta wersja działa z 2.3.0 wersji Typescript--strict tryb. Nie działa z zagnieżdżonymi tablicami (nie jest rekurencyjny). Frederik Aalund
'apply' spowoduje przepełnienie stosu na dużych wejściach dla niektórych vms, jak v8. To naprawdę nie jest przeznaczone do tego przypadku użycia. user1009908
Uwaga: ta odpowiedź spłaszcza tylko jeden poziom. Dla rekurencyjnego spłaszczenia zobacz odpowiedź @Trindaz. Phrogz
Dalej do komentarza @ Seana: składnia ES6 czyni to super zwięzłym:var merged = [].concat(...arrays) Sethi
9
ES6 One Line Flatten

Widziećlodash spłaszczyć, podkreślenie spłaszcz (płytkietrue)

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

lub

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

Przetestowany z

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

Widziećlodash flattenDeep, podkreślenie spłaszcz

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

Przetestowany z

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]);
});
Twój drugi przykład jest lepiej napisany jakoArray.prototype.concat.apply([], arr) ponieważ tworzysz dodatkową tablicę, aby dostać się doconcat funkcjonować. Środowiska wykonawcze mogą, ale nie muszą, zoptymalizować go, gdy go uruchomią, ale dostęp do funkcji w prototypie nie wygląda na brzydszy niż to w każdym przypadku. Mörre
34

Nowa natywna metoda ECMA 2018 nazywa sięmieszkanie zrobić to dokładnie.

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]]
Myślę, że teraz możemy to rozważyć ... ponieważ teraz jest częścią standardu (2019) .. czy możemy raz jeszcze odwiedzić tę część wydajności? Yuvaraj
-1; nie, to nie jest częściąECMAScript 2018. To wciąż tylko propozycja, która nie dotarła do żadnej specyfikacji ECMAScript. Mark Amery
Szkoda, że ​​nie ma go nawet na pierwszej stronie odpowiedzi. Ta funkcja jest dostępna w Chrome 69 i Firefox 62 (oraz w węźle 11 dla osób pracujących w zapleczu) Matt M.
5

Zrobiłem to za pomocą rekursji i zamknięć

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;
}
@dashambles jest poprawne - różnica polega na tym, że gdyby to było zamknięcie, zwracałoby się wewnętrzną funkcję na zewnątrz, a gdy funkcja zewnętrzna jest zakończona, nadal można korzystać z funkcji wewnętrznej, aby uzyskać dostęp do jej zakresu. Tutaj czas życia funkcji zewnętrznej jest dłuższy niż funkcji wewnętrznej, więc „zamknięcie” nigdy nie jest tworzone. Mörre
Prosta i słodka, ta odpowiedź działa lepiej niż zaakceptowana odpowiedź. Spłaszcza głęboko zagnieżdżone poziomy, a nie tylko pierwszy poziom Om Shankar
AFAIK to leksykalny zakres, a nie zamknięcie dashambles
163

Najlepiej można to zrobić za pomocą funkcji zmniejszania javascript.

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

Lub za pomocą ES2015:

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

js-skrzypce

Dokumenty Mozilli

Ponieważ używasz ES6, możesz również użyćoperator-spread jako literał tablicy. arrays.reduce((flatten, arr) => [...flatten, ...arr]) Putzi San
@Wllang Nie,Function.prototype.a‌​pply spowoduje przepełnienie stosu dla (bardzo) dużych tablic. Więc aniapply ani operator rozprzestrzeniania nie jest alternatywą dlareduce w tym przypadku. user6445533
Użyłbym również zmniejszenia, ale jedną z interesujących rzeczy, których nauczyłem się z tego fragmentu, jest toprzez większość czasu nie musisz przekazywać wartości początkowej :) José F. Romaniello
Właściwie jest to zrobione przezconcat niereduce, reduce jest tu zbędny! Ali Shakiba
5

Byłam głupiaGeneratory ES6 innego dnia i napisałemten sens. Który zawiera...

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

Zasadniczo tworzę generator, który zapętla oryginalną tablicę wejściową, jeśli znajdzie tablicę, z której korzystawydajność* operator w połączeniu z rekurencją, aby stale spłaszczać wewnętrzne tablice. Jeśli element nie jest tablicą, wystarczyplony pojedynczy przedmiot. Następnie za pomocąOperator rozsiewu ES6 (aka operator splat) Spłaszczam generator do nowej instancji tablicy.

Nie testowałem wydajności tego, ale uważam, że jest to miły prosty przykład użycia generatorów i operatora yield *.

Ale znowu byłem głupi, więc jestem pewien, że jest na to więcej sposobów.

Podobna odpowiedź (przy użyciu generatora i delegowania wydajności), ale wFormat PHP The Red Pea
24

A co z używaniemreduce(callback[, initialValue]) metodaJavaScript 1.8

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

Zrobiłbym to.

Nie potrzebujesz drugiego argumentu w naszym przypadku prostego[[1], [2,3]].reduce( (a,b) => a.concat(b)) Umair Ahmed
[[1], [2,3]].reduce( (a,b) => a.concat(b), [] ) jest bardziej sexy. golopot
3

Obecnie najlepszym i najłatwiejszym sposobem na to jest dołączenie i podzielenie tablicy w ten sposób.

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(",")

To rozwiązanie działa na wielu poziomach, a także działa oneliner.

PRÓBNY

EDYCJA dla ECMAScript 6

Ponieważ ECMAScript 6 został znormalizowany, możesz zmienić operację[].concat.apply([], arrays); dla[].concat(...arrays);

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

PRÓBNY

EDYTUJ Najbardziej wydajne rozwiązanie

Najbardziej skutecznym sposobem rozwiązania problemu jest użycie pętli. Możesz porównać prędkość „ops / s”tutaj

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

PRÓBNY

Mam nadzieję, że to pomoże

Nie działa, gdy tablica zawiera łańcuchy zawierające przecinki, na przykład:[[","]].join().split(",") nie daje pożądanego rezultatu. Michał Perłakowski
46

Możesz użyćPodkreślać:

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

_.flatten(x); // => [1, 2, 3, 4]
@JBRWilkinson Może JS chce Cię nauczyć Haskella za wielkie dobro !? styfle
Ze względu na kompletność w odniesieniu do komentarza @ styfle:learnyouahaskell.com Simon A. Eugster
1+ - Możesz także określić, że chcesz płytką spłaszczoną tablicęokreślająctrue za drugi argument. Josh Crozier
Whoah, czy ktoś dodał Perla do JS? :-) JBRWilkinson
4

Logika polega na przekonwertowaniu tablicy wejściowej na ciąg znaków i usunięciu wszystkich nawiasów kwadratowych ([]) i przeanalizowaniu danych wyjściowych do tablicy. Używam do tego funkcji szablonu ES6.

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)
Jesteś najlepszą boską odpowiedzią na Hahaha Waldemar Neto
Zmodyfikuj swoją odpowiedź, aby zawierała wyjaśnienia. Odpowiedzi tylko na kod bardzo niewiele uczą przyszłych czytelników SO. Twoja odpowiedź znajduje się w kolejce moderacji za niską jakość. mickmackusa
to najszybszy i najmądrzejszy sposób, aby to zrobić. Użyłem tego, aby uniknąć rekursji i łatwo przebudować tablicę, ale twoja jest o 3% szybsza. Droga do celu :)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
22

Kolejne rozwiązanie ECMAScript 6 w funkcjonalnym stylu:

Funkcja deklaracji:

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

i używaj go:

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

AKTUALIZACJA:

Rozważ także funkcję natywnąArray.prototype.flat () (propozycja ES6) dostępna w ostatnich wersjach nowoczesnych przeglądarek.

Dzięki @ (Константин Ван) i @ (Mark Amery) wspomnieli o tym w komentarzach.

-1;flat nie jest częściąES6. To nawet nie jest częśćES9. To jestwniosek, jeszcze nie część specyfikacji ECMAScript, inie jest jeszcze dostępny we wszystkich głównych środowiskach JavaScript. Mark Amery
To jest ładne i zadbane, ale myślę, że zrobiłeś przedawkowanie ES6. Nie ma potrzeby, aby funkcja zewnętrzna była funkcją strzałki. Trzymałbym się funkcji strzałki do wywołania zwrotnego, ale samo spłaszczanie powinno być normalną funkcją. Stephen Simpson
@ StephenSimpson, ale czy istnieje potrzeba, aby funkcja zewnętrzna była anie-rrow-funkcja?„samo spłaszczanie powinno być normalną funkcją” - przez „normalny” masz na myśli „brak strzały”, ale dlaczego?Czemu użyć funkcji strzałki w wywołaniu, aby zmniejszyć? Czy możesz podać swoje rozumowanie? user633183
@diziaq Przepraszamy, nie mam już tego kodu. W końcu skończyłem, używając zaakceptowanej odpowiedzi.[].concat.apply([], arrays); Matt Westlake
29

Rozwiązanie dla bardziej ogólnego przypadku, gdy w macierzy możesz mieć kilka elementów innych niż tablica.

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;
}
Spowoduje to przerwanie, jeśli ręcznie przekażemy drugi argument. Na przykład spróbuj tego:flattenArrayOfArrays (arr, 10) albo toflattenArrayOfArrays(arr, [1,[3]]); - te drugie argumenty są dodawane do wyjścia. Om Shankar
ta odpowiedź nie jest kompletna! wynik rekurencji nigdy nie jest przypisywany nigdzie, więc wynik rekursji zostaje utracony r3wt
Podejście to było bardzo efektywne w spłaszczaniu zagnieżdżonej formy tablicowej zestawów wyników uzyskanych z aZapytanie JsonPath. kevinjansz
@ r3wt poszedł do przodu i dodał poprawkę. Musisz upewnić się, że bieżąca tablica, którą utrzymujesz,r, faktycznie połączy wyniki z rekurencji. aug
Dodano jako metodę tablic: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
5

Podejście 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);

Zgadzam się z @monners. Bez dwóch zdań najbardziej eleganckie rozwiązanie w tym wątku. Nicolás Fantone
Dlaczego ta odpowiedź nie ma więcej głosów? !! monners
5

po prostu najlepsze rozwiązanie bez lodash

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

Aby spłaszczyć tablicę tablic pojedynczych elementów, nie trzeba importować biblioteki, prosta pętla jest zarówno najprostsza, jak inajbardziej wydajny rozwiązanie :

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

Dla downvoters: przeczytaj pytanie, nie przejmuj się, ponieważ nie pasuje do twojego bardzo odmiennego problemu. To rozwiązanie jest zarówno najszybszym, jak i najprostszym rozwiązaniem dla zadanego pytania.

over-engineering nie zawsze pasuje do rachunku. Ta odpowiedź pokazuje inną technikę spłaszczania prostych tablic (na przykład przykład OP), co czasami jest wymagane. rlemon
Proszę powstrzymać się od osobistych ataków w komentarzach. @OmShankar, podczas gdy rozwiązanie nie jest idealne, działa dosłownie na pytanie, co czyni je poprawnym. Jeśli nie lubisz tego, zagłosuj w dół i opcjonalnie wyjaśnij dlaczego, bez „spełniania standardów SO” lub „odrzucenia w wywiadzie”. To nie są ważne argumenty, dlaczego odpowiedź jest zła. Madara Uchiha
Powiedziałbym: „Nie szukaj rzeczy bardziej tajemniczych”. ^^ user166390
„tajemniczy”… Poważnie… Nawet najmłodszy „programista” nie powinien znaleźć „tajemniczej” odwróconej pętli. Denys Séguret
14

Proszę zanotować: GdyFunction.prototype.apply ([].concat.apply([], arrays)) lub operatora rozprzestrzeniania ([].concat(...arrays)) jest używany do spłaszczenia tablicy, obie mogą powodować przepełnienie stosu dla dużych tablic, ponieważ każdy argument funkcji jest przechowywany na stosie.

Oto bezpieczna dla stosu implementacja w stylu funkcjonalnym, która waży najważniejsze wymagania względem siebie:

możliwość ponownego użyciaczytelnośćzwięzłośćwydajność

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

Gdy tylko przyzwyczaisz się do małych funkcji strzałkowych w formie curried, składu funkcji i funkcji wyższego rzędu, kod ten będzie wyglądał jak proza. Programowanie polega jedynie na zestawieniu małych elementów konstrukcyjnych, które zawsze działają zgodnie z oczekiwaniami, ponieważ nie zawierają żadnych skutków ubocznych.

@Daerdemandt Ale jeśli napiszesz to jako oddzielne funkcje, prawdopodobnie będziesz mógł je ponownie wykorzystać w innym kodzie. Michał Perłakowski
Ha ha. Całkowicie szanuj swoją odpowiedź, chociaż czytanie programowania funkcjonalnego w ten sposób wciąż przypomina mi czytanie japońskiego znaku po znaku (mówiący po angielsku). Tarwin Stroh-Spijer
Jeśli uważasz, że implementujesz cechy języka A ​​w języku B nie jako część projektu, którego jedynym celem jest właśnie to, to ktoś gdzieś skręcił w niewłaściwym kierunku. Czy to możesz być ty? Po prostu z tymconst flatten = (arr) => arr.reduce((a, b) => a.concat(b), []); oszczędza ci wizualne śmiecii wyjaśnienie członkom drużyny, dlaczego potrzebujesz 3 dodatkowych funkcjii niektóre wywołania funkcji też. Daerdemandt
@ MichałPerłakowski Jeśli musisz użyć ich w kilku miejscach, nie wymyślaj na nowo koła i wybierz pakiette - udokumentowane i wspierane przez inne osoby. Daerdemandt
7

Jeśli masz tylko tablice z 1 elementem łańcuchowym:

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

wykona pracę. Bt, który szczególnie pasuje do Twojego kodu.

Uwielbiam to rozwiązanie =) Huei Tan
@ Anonimowy Nie odebrałem tego, ponieważ technicznie spełnia wymagania pytania, ale jest prawdopodobne, ponieważ jest to dość kiepskie rozwiązanie, które nie jest przydatne w ogólnym przypadku. Biorąc pod uwagę, ile jest lepszych rozwiązań, nigdy bym nie polecił komuś, żeby poszedł z tym, ponieważ przerywa moment, w którym masz więcej niż jeden element lub gdy nie są one ciągami. Thor84no
Nie obsługuje tylko tablic z 1 elementami łańcuchowymi, obsługuje również tę tablicę['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',') alucic
Ktokolwiek głosował, proszę wyjaśnić dlaczego. Szukałem przyzwoitego rozwiązania i wszystkich rozwiązań, które najbardziej mi się podobały. Anonymous
52

Aktualizacja: okazało się, że to rozwiązanie nie działa z dużymi tablicami. Szukasz lepszego, szybszego rozwiązania, sprawdźta odpowiedź.

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

Po prostu się rozszerzaarr i przekazuje to jako argumentyconcat(), który łączy wszystkie tablice w jedną. To jest równoważne[].concat.apply([], arr).

Możesz również spróbować tego dla głębokiego spłaszczenia:

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

Zobacz prezentacjęJSBin.

Odniesienia do elementów ECMAScript 6 użytych w tej odpowiedzi:

Operator rozłożeniaFunkcje strzałek

Uwaga dodatkowa: metody takie jakfind() i funkcje strzałek nie są obsługiwane przez wszystkie przeglądarki, ale nie oznacza to, że nie możesz teraz korzystać z tych funkcji. Po prostu użyjBabel - przekształca kod ES6 w ES5.

Ponieważ prawie wszystkie odpowiedzi tutaj nadużywająapply w ten sposób usunąłem moje komentarze z twojego. Nadal myślę o używaniuapply/ rozprzestrzenianie się w ten sposób jest złe, ale ponieważ nikt się nie przejmuje ... user6445533
@ LUH3417 To nie tak, naprawdę doceniam twoje komentarze. Okazało się, że masz rację - to rozwiązanie rzeczywiście nie działa z dużymi tablicami. Zamieściłemkolejna odpowiedź który działa dobrze nawet z tablicami 200 000 elementów. Michał Perłakowski
@GEMI Na przykład próba spłaszczenia tablicy 500000 elementów za pomocą tej metody daje „RangeError: Przekroczono maksymalny rozmiar stosu wywołań”. Michał Perłakowski
Co masz na myśli „nie działa z dużymi tablicami”? Jak duży? Co się dzieje? GEMI
Jeśli korzystasz z ES6, możesz dodatkowo zmniejszyć:const flatten = arr => [].concat(...arr) Eruant
64

Większość odpowiedzi tutaj nie działa na ogromnych (np. 200 000 elementów) tablicach, a nawet jeśli tak, to są powolne.odpowiedź polkovnikov.ph ma najlepszą wydajność, ale nie działa w przypadku głębokiego spłaszczania.

Tutaj jestnajszybsze rozwiązanie, które działa również na tablicach z wieloma poziomami zagnieżdżania:

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;
};
PrzykładyOgromne tablice
flatten(Array(200000).fill([1]));

Obsługuje ogromne tablice. Na moim komputerze wykonanie tego kodu zajmuje około 14 ms.

Zagnieżdżone tablice
flatten(Array(2).fill(Array(2).fill(Array(2).fill([1]))));

Działa z zagnieżdżonymi tablicami. Ten kod generuje[1, 1, 1, 1, 1, 1, 1, 1].

Tablice z różnymi poziomami zagnieżdżania
flatten([1, [1], [[1]]]);

Nie ma żadnych problemów ze spłaszczaniem macierzy takich jak ta.

@ 0xcaff W Chrome nie działa w ogóle z tablicą 200 000 elementów (dostajeszRangeError: Maximum call stack size exceeded). Dla tablicy 20 000 elementów zajmuje to 2-5 milisekund. Michał Perłakowski
jaka jest złożoność tej notacji? George Katsanos
@ MichałPerłakowski właśnie to przetestowałemstackoverflow.com/a/49895394/4948383 z ogromną tablicą i działa. Manoj Yadav
Zazwyczaj, gdy tablica jest generowana z treści generowanych przez użytkownika. nitely

Powiązane pytania