Bir dizi diziyi birleştir / düzleştir


1104

Bir JavaScript dizisi var:

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

Ayrı iç dizileri aşağıdaki gibi bir araya getirmeye nasıl giderim:

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


17
reduce+ Kullanan tüm çözümler concatkabul edilmiş bir yanıt olarak (yalnızca bir çağrı concat) kötü bir tarayıcıda en fazla O (N * 2) ve bir tarayıcıda O (N) olacak şekilde O ((N ^ 2) / 2 ) 'dir. İyi bir. Ayrıca Denys çözümü asıl soru için optimize edilmiştir ve tek sorudan 2 kata kadar daha hızlıdır concat. İçin reducemillet 's eğlenceli dizisi tüm azaltmak + concat çözümleri yapıyor olurdu 1000 bir unsuru Altdizilim olsaydı serin minik kod yazmadan ancak örneğin hissetmek 500.500 operasyonları nereye 1.000 işlemleri yapacağını tek concat veya basit döngü olarak.
gman

12
Sadece kullanıcılara yayılan operatör[].concat(...array)
Oleg Dater

@gman reduce + concat solution O ((N ^ 2) / 2) nasıl bulunur? stackoverflow.com/questions/52752666/… farklı bir karmaşıklıktan bahsediyor.
Usman

4
ES2019'u destekleyen en son tarayıcılarla : düzleştirmek için maksimum derinlik array.flat(Infinity)nerede Infinity.
Timothy Gu

Yanıtlar:


1860

concatDizileri birleştirmek için kullanabilirsiniz :

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

console.log(merged);

applyYöntemini kullanmak, concatikinci parametreyi bir dizi olarak alır, bu nedenle son satır bununla aynıdır:

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

Ayrıca, Array.prototype.flat()dizileri düzleştirmek için kullanabileceğiniz (ES2019'da tanıtılan) yöntem de vardır , ancak Internet Explorer'da yalnızca 11 sürümünden başlayarak Node.js'de kullanılabilir .

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
Not concatböylece, kaynak dizisi değiştirmez mergeddizi çağrısının ardından boş kalacak concat. Gibi bir şey söylemek daha iyi:merged = merged.concat.apply(merged, arrays);
Nate

65
var merged = [].concat.apply([], arrays);bir hatta almak için iyi çalışıyor gibi görünüyor. edit: Nikita'nın cevabı zaten gösterdiği gibi.
Sean

44
Veya Array.prototype.concat.apply([], arrays).
danhbear

30
Not: Bu cevap sadece bir seviye derinleşir. Özyinelemeli düzleştirme için @Trindaz tarafından verilen cevaba bakın.
Phrogz

209
@ Sean'ın yorumuna ek olarak: ES6 sözdizimi bu süper özlü hale getiriyor:var merged = [].concat(...arrays)
Sethi

505

Burada, n boyutlu bir diziyi düzleştirmek için yeni JavaScript dizisi yöntemlerinden bazılarını kullanan kısa bir işlev verilmiştir.

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

Kullanımı:

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
Bu yaklaşımı seviyorum. Çok daha genel ve iç içe dizileri destekler
alfredocambera

10
Bu çözüm için bellek kullanım profili nedir? Kuyruk özyineleme sırasında çok fazla ara dizi oluşturuyor gibi görünüyor ....
JBRWilkinson

7
@ayjay, azaltma işlevi için başlangıç ​​akümülatör değeri, ne mdn başlangıç değerini çağırır. Bu durumda flat, geçilen anonim işleve yapılan ilk çağrıdaki değerdir reduce. Belirtilmezse, reduceilk değeri dizinin dışına bağlayan ilk çağrı flat, sonuçta her iki örnekte de 1bağlanır flat. 1.concatbir işlev değildir.
Noah Freitas

19
Veya daha kısa, daha seksi bir biçimde: const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
Tsvetomir Tsonev

12
Keyfi yumurtlamak için @TsvetomirTsonev ve Nuh'un çözümlerine riffing:const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
Will

314

Orijinali değiştirmeden yeni bir dizi oluşturan kafa karıştırıcı bir şekilde gizli bir yöntem var:

var oldArray = [[1],[2,3],[4]];
var newArray = Array.prototype.concat.apply([], oldArray);
console.log(newArray); // [ 1, 2, 3, 4 ]


11
CoffeeScript'te bu[].concat([[1],[2,3],[4]]...)
amoebe

8
@ amoebe cevabınız [[1],[2,3],[4]]sonuç verir . @Nikita'nın verdiği çözüm, JS'nin yanı sıra CoffeeScript için de doğrudur.
Ryan Kennedy

5
Ahh, hatasını buldum. Gösterinizde ekstra bir çift köşeli parantez var, olmalı [].concat([1],[2,3],[4],...).
Ryan Kennedy

21
Sanırım hatasını da buldum. ...Gerçek kod, bazı üç nokta nokta vardır.
amoebe

3
Bir kütüphane işlevinin kullanılması, daha az zorunlu olan “karışıklık” olduğu anlamına gelmez. Sadece karmaşa bir kütüphanenin içinde saklı. Açıkçası bir kütüphane kullanmak kendi işlevinizi yuvarlamaktan daha iyidir, ancak bu tekniğin "zorunlu karışıklığı" azalttığını iddia etmek oldukça saçmadır.
paldepind

204

En iyi javascript azaltma fonksiyonu ile yapılabilir.

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

Veya ES2015 ile:

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

js-keman

Mozilla belgeleri


1
@JohnS Aslında dönüş başına feed'leri tek bir (dizi) değere dönüştürün. Kanıt? reduce () işlevini çıkarın ve çalışıp çalışmadığına bakın. reduce () burada Function.prototype.apply () 'ye bir alternatiftir
André Werlang

3
Ben de azaltmak kullanmış olurdu ama bu pasajdan öğrendiğim ilginç bir şey çoğu zaman bir başlangıç ​​değeri geçmesine gerek yok olmasıdır :)
José F. Romaniello

2
Reduce ile ilgili sorun dizinin boş olamayacağıdır, bu yüzden fazladan bir doğrulama eklemeniz gerekecektir.
calbertts

4
@calbertts Sadece başlangıç ​​değerini iletin []ve başka doğrulama gerekmez.

8
ES6 kullandığınızdan , yayılma işlecini dizi değişmezi olarak da kullanabilirsiniz . arrays.reduce((flatten, arr) => [...flatten, ...arr])
Putzi San

109

Bunu tam olarak yapmak için flat adı verilen yeni bir yerel yöntem var .

(2019'un sonlarından flatitibaren ECMA 2019 standardında yayınlanmaktadır ve core-js@3(babel'in kütüphanesi ) çoklu dolum kütüphanesine dahil etmektedir )

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
Bu, cevapların ilk sayfasında bile olmayan bir utanç. Bu özellik Chrome 69 ve Firefox 62'de (ve arka uçta çalışanlar için Düğüm 11) kullanılabilir
Matt M.


2
1; hayır, bu ECMAScript 2018'in bir parçası değil . Hala herhangi bir ECMAScript spesifikasyonunu yapmayan bir teklif.
Mark Amery

1
Sanırım şimdi bunu düşünebiliriz ... çünkü şimdi standardın (2019) bir parçası .. bunun performans kısmını bir kez tekrar ziyaret edebilir miyiz?
Yuvaraj

Yine de herhangi bir Microsoft tarayıcısı tarafından desteklenmiyor gibi görünüyor (en azından bu yorumu yazdığım sırada)
Laurent S.

78

Buradaki cevapların çoğu devasa (örn. 200.000 eleman) dizilerde işe yaramıyor ve öyle olsalar bile yavaşlar. polkovnikov.ph'nin cevabı en iyi performansa sahiptir, ancak derin düzleştirme için işe yaramaz.

İşte birden fazla yuvalama düzeyine sahip dizilerde de çalışan en hızlı çözüm :

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

Örnekler

Büyük diziler

flatten(Array(200000).fill([1]));

Büyük dizileri iyi işler. Makinemde bu kodun yürütülmesi yaklaşık 14 ms sürüyor.

İç içe diziler

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

İç içe dizilerle çalışır. Bu kod üretir [1, 1, 1, 1, 1, 1, 1, 1].

Farklı iç içe yerleştirme düzeylerine sahip diziler

flatten([1, [1], [[1]]]);

Bunun gibi düzleştirici dizilerle herhangi bir sorunu yoktur.


Dışında büyük dizi oldukça düz. Bu çözüm, derin iç içe diziler için işe yaramaz. Hiçbir özyinelemeli çözüm olmaz. Aslında Safari'nin şu anda TCO'su yok, bu yüzden özyinelemeli algoritma iyi performans göstermeyecek.
Mart'ta

@nitely Ama hangi gerçek dünya durumunda birkaç seviye iç içe yerleştirilmiş diziler olurdu?
Michał Perłakowski

2
Genellikle, dizi kullanıcı tarafından oluşturulan içerikten oluşturulduğunda.
nitely

@ 0xcaff Chrome'da, 200.000 elementlik bir dizi ile hiç çalışmaz RangeError: Maximum call stack size exceeded. 20 000 elementlik dizi için 2-5 milisaniye gerekir.
Michał Perłakowski

3
Bunun O gösterim karmaşıklığı nedir?
George Katsanos

58

Güncelleme: Bu çözümün büyük dizilerle çalışmadığı ortaya çıktı. Daha iyi, daha hızlı bir çözüm arıyorsanız, bu cevaba göz atın .


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

Basitçe genişler arrve concat()tüm dizileri bir araya getiren argüman olarak iletir. Eşdeğerdir [].concat.apply([], arr).

Bunu derin düzleştirme için de deneyebilirsiniz:

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 ile ilgili demoya bakın .

Bu cevapta kullanılan ECMAScript 6 öğeleri için referanslar:


Yan not: find()ve ok işlevleri gibi yöntemler tüm tarayıcılar tarafından desteklenmez, ancak şu anda bu özellikleri kullanamayacağınız anlamına gelmez. Sadece Babel kullanın - ES6 kodunu ES5'e dönüştürür.


Buradaki yanıtların neredeyse tamamı applybu şekilde kötüye kullanıldığından , yorumlarımı sizden kaldırdım. Ben hala applybu şekilde kullanarak / yaymak kötü tavsiye düşünüyorum, ama kimse umursamıyor beri ...

@ LUH3417 Öyle değil, yorumlarınızı gerçekten takdir ediyorum. Haklısın - bu çözüm gerçekten büyük dizilerle çalışmaz. 200.000 elementlik dizilerde bile iyi çalışan bir cevap daha yayınladım .
Michał Perłakowski

ES6 kullanıyorsanız, aşağıdakileri de const flatten = arr => [].concat(...arr)
azaltabilirsiniz

Ne demek "büyük dizilerle çalışmaz"? Ne kadar büyük? Ne oluyor?
GEMI

4
@GEMI Örneğin, bu yöntemi kullanarak 500000 elemanlık bir diziyi düzleştirmeye çalışmak "RangeError: Maksimum çağrı yığını boyutu aşıldı" değerini verir.
Michał Perłakowski

51

Alt Çizgi'yi kullanabilirsiniz :

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

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

20
Kim, JS'ye Perl ekledi mi? :-)
JBRWilkinson

9
@JBRWilkinson Belki JS Size Büyük Fayda için Haskell Öğrenmek İstiyor !?
styfle

1+ - İkinci bağımsız değişken için belirterektrue sığ düzleştirilmiş bir dizi istediğinizi de belirtebilirsiniz .
Josh Crozier

7
@ Styfle'nin yorumuyla ilgili bütünlük uğruna: learnyouahaskell.com
Simon A. Eugster

41

Genel prosedürler, belirli bir davranışı her kullanmamız gerektiğinde karmaşıklığı yeniden yazmak zorunda olmadığımız anlamına gelir.

concatMap(veya flatMap) tam da bu durumda ihtiyacımız olan şeydir.

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

öngörü

Ve evet, sadece düzleştirir doğru tahmin biri o tam olarak nasıl olduğunu seviyesini, gerektiği çalışmak

Bunun gibi bir veri kümesi düşünün

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

Tamam, şimdi katılacak tüm oyuncuları gösteren bir liste yazdırmak istediğimizi söyleyin game

const gamePlayers = game =>
  flatten (game)

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

Bizim ise flattenprosedürün fazla iç içe diziler düzleştirilmiş, biz bu çöp sonucu ile bitirmek istiyorum ...

const gamePlayers = game =>
  badGenericFlatten(game)

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

derin yuvarlanıyor bebeğim

Bu bazen iç içe dizileri de düzleştirmek istemediğiniz anlamına gelmez - bu sadece varsayılan davranış olmamalıdır.

deepFlattenKolaylıkla bir prosedür yapabiliriz …

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

Orada. Artık her bir iş için bir aracınız var - biri bir seviye iç içe yerleştirmeyi ezmek için flattenve diğeri tüm iç içe geçmişleri silmek içindeepFlatten .

Belki adlandırabilir obliterateveya nukeadını beğenmediyseniz deepFlatten.


İki kez yineleme!

Tabii ki yukarıdaki uygulamalar akıllı ve özlüdür, ancak .mapbunu bir çağrıyı kullanmak .reduceaslında gereğinden fazla yineleme yaptığımız anlamına gelir

Aradığım güvenilir bir birleştirici kullanmak mapReduceyinelemeleri bir minumda tutmaya yardımcı olur; bir eşleme işlevi m :: a -> b, bir azaltma işlevi alır r :: (b,a) ->bve yeni bir azaltma işlevi döndürür - bu birleştirici dönüştürücülerin kalbinde yer alır ; eğer ilgileniyorsanız, onlar hakkında başka cevaplar yazdım

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


Sık sık, cevaplarını gördüğümde benimkini geri çekmek istiyorum, çünkü değersiz hale geldiler. Mükemmel cevap! concatkendisi sadece yığını havaya uçurmaz ...ve applyyapar (çok büyük dizilerle birlikte). Görmedim. Şu anda kendimi çok kötü hissediyorum.

@ LUH3417 çok kötü hissetmemelisin. Ayrıca, iyi yazılmış açıklamalarla iyi yanıtlar da sağlarsınız. Buradaki her şey bir öğrenme deneyimidir - sıklıkla hatalar yaparım ve kötü cevaplar da yazarım. Onları birkaç ay içinde tekrar ziyaret edip "ne düşünüyordum?" ve sonunda tamamen gözden geçiririz. Hiçbir cevap mükemmel değildir. Hepimiz bir öğrenme eğrisinin bir noktasındayız ^ _ ^
Teşekkürler

1
concatJavascript'te Haskell'den farklı bir anlama geldiğini lütfen unutmayın . Haskell's concat( [[a]] -> [a]) flattenJavascript içinde çağrılır ve foldr (++) [](Javascript: foldr(concat) ([])curried fonksiyonlar varsayılarak) olarak uygulanır. Javascript en concattuhaf bir ek ( (++)Haskell içinde), hem [a] -> [a] -> [a]ve işleyebilir a -> [a] -> [a].

Sanırım daha iyi bir isim vardı flatMap, çünkü tam olarak budur concatMap: Monad bindörneği list. concatpMapolarak uygulanır foldr ((++) . f) []. Javascript çevrildi: const flatMap = f => foldr(comp(concat) (f)) ([]). Bu tabii ki uygulamanıza benzer comp.

bu algoritmanın karmaşıklığı nedir?
George Katsanos

32

Dizinizde dizi olmayan öğelerin olabileceği daha genel durum için bir çözüm.

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
Bu yaklaşım, bir JsonPath sorgusundan aldığınız sonuç kümelerinin iç içe dizi formunu düzleştirmede çok etkili oldu .
kevinjansz

1
Diziler yöntemi olarak eklendi: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

İkinci argümanı el ile geçersek bu kırılacaktır. Örneğin, şunu deneyin: flattenArrayOfArrays (arr, 10)veya bu flattenArrayOfArrays(arr, [1,[3]]);- bu ikinci bağımsız değişkenler çıktıya eklenir.
Om Shankar

2
bu cevap tam değil! özyineleme sonucu hiçbir yere atanmaz, bu nedenle özyineleme sonucu kaybolur
r3wt

1
@ r3wt devam etti ve bir düzeltme ekledi. Yapmanız gereken, devam etmekte olduğunuz geçerli dizinin r, özyinelemeden elde edilen sonuçları birleştireceğinden emin olmaktır .
ağustos

29

Bir dizi tek eleman dizisini düzleştirmek için bir kitaplığı içe aktarmanıza gerek yoktur, basit bir döngü hem en basit hem de en verimli çözümdür:

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

Downvoters için: lütfen soruyu okuyun, çok farklı probleminize uymadığı için downvote etmeyin. Bu çözüm, sorulan soru için hem en hızlı hem de en basit çözümdür.


4
"Daha mahrem şeyler aramayın" derdim. ^^

4
Demek istediğim ... bunun için bir kütüphane kullanmalarını tavsiye eden insanlar gördüğümde ... bu çılgınca ...
Denys Séguret

3
Ahh, sadece bunun için bir kütüphane kullanmak aptalca olurdu ... ama zaten mevcutsa.

9
@dystroy for döngüsünün koşullu kısmı okunabilir değil. Yoksa sadece ben miyim: D
Andreas

4
Ne kadar şifreli olduğu gerçekten önemli değil. Bu kod, bunun "düzleştiren" ['foo', ['bar']]için ['f', 'bar'].
jonschlinkert

29

reduce(callback[, initialValue])Yöntemini kullanmaya ne dersinizJavaScript 1.8

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

İşi yapar.


8
[[1], [2,3]].reduce( (a,b) => a.concat(b), [] )daha seksi.
golopot

5
Bizim durumumuzda ikinci tartışmaya gerek yok[[1], [2,3]].reduce( (a,b) => a.concat(b))
Umair Ahmed

29

İşlevsel tarzda bir başka ECMAScript 6 çözümü:

Bir işlev bildirin:

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

ve kullanın:

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

Modern tarayıcıların son sürümlerinde bulunan yerel bir işlev olan Array.prototype.flat () (ES6 için öneri ) de düşünün . @ (Константин Ван) ve @ (Mark Amery) sayesinde yorumlarda belirtti.

flatİşlev, bir parametre, eşit bir dizi iç içe geçme, beklenen derinliğinin önceden var 1varsayılan.

[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
Bu güzel ve temiz ama bence aşırı doz ES6 yaptınız. Dış fonksiyonun ok fonksiyonu olmasına gerek yoktur. Ben geri arama azaltmak için ok-fonksiyonu ile sopa olurdu ama düzleştirmek kendini normal bir işlev olmalıdır.
Stephen Simpson

3
@StephenSimpson, ancak dış işlevin, yarık olmayan bir işlev olması gerekiyor mu? "kendini düzleştirmek normal bir işlev olmalıdır" - "normal" demekle "oksuz" demek istiyorsun, ama neden? O zaman azaltmak için neden bir ok işlevi kullanıyorsunuz? Muhakeme hattınızı tedarik edebilir misiniz?
Teşekkürler

@naomik Akıl yürütmem gereksiz olması. Esas olarak bir stil meselesi; Yorumumda çok daha net olmalıyım. Birini veya diğerini kullanmak için önemli bir kodlama nedeni yoktur. Ancak, fonksiyonun ok olmayan olarak görülmesi ve okunması daha kolaydır. İç fonksiyon, daha kompakt olduğundan (ve elbette bağlam oluşturulmadığından) ok fonksiyonu olarak kullanışlıdır. Ok fonksiyonları, kompakt, okunması kolay fonksiyon oluşturmak ve bu karışıklığı önlemek için mükemmeldir. Bununla birlikte, ok olmayan bir zaman yeterli olduğunda okumayı gerçekten zorlaştırabilirler. Diğerleri buna katılmayabilir!
Stephen Simpson

BaşlarkenRangeError: Maximum call stack size exceeded
Matt Westlake

@Matt, lütfen hatayı yeniden oluşturmak için kullandığınız kanıtları paylaşın
diziaq

22
const common = arr.reduce((a, b) => [...a, ...b], [])

Bu benim kod boyunca bunu nasıl ama çok uzun bir dizi varsa hız kabul edilen cevap karşılaştırır emin değilim
Michael Aaron Wilson

14

Lütfen dikkat: Bir diziyi düzleştirmek için Function.prototype.apply( [].concat.apply([], arrays)) veya forma operatörü ( [].concat(...arrays)) kullanıldığında, her iki işlev de büyük diziler için yığın taşmasına neden olabilir, çünkü bir işlevin her argümanı yığınta depolanır.

İşte fonksiyonel tarzda birbirine karşı en önemli gereksinimleri ağırlaştıran, yığın halinde güvenli bir uygulama:

  • tekrar Kullanılabilirlik
  • okunabilirliği
  • özlülük
  • verim

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

Körüklü form, fonksiyon kompozisyonu ve yüksek dereceli fonksiyonlardaki küçük ok fonksiyonlarına alışmaya başlar başlamaz bu kod düz yazı gibi okunur. Programlama daha sonra sadece beklendiği gibi çalışan küçük yapı taşlarının bir araya getirilmesinden oluşur, çünkü herhangi bir yan etki içermezler.


1
Haha. Bunun gibi fonksiyonel programlamayı okumak hala Japonca karakter karakter okumak (İngilizce konuşmacı) gibi olsa da, cevabınıza tamamen saygı gösterin.
Tarwin Stroh-Spijer

2
Kendinizi A dilinin özelliklerini B dilinde yapmanın tek amacı tam olarak bunu yapmakla projenin bir parçası olarak görmüyorsanız, o zaman bir yerdeki biri yanlış bir dönüş yapmıştı. Sen olabilirdin? Sadece devam etmek const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);size 3 ekstra fonksiyona ve bazı fonksiyon çağrılarına neden ihtiyaç duyduğunuzda görsel çöp ve takım arkadaşlarınıza açıklama kazandırır .
Daerdemandt

3
@Daerdemandt Ancak bunu ayrı işlevler olarak yazarsanız, büyük olasılıkla bunları başka bir kodda yeniden kullanabilirsiniz.
Michał Perłakowski

@ MichałPerłakowski Bunları birkaç yerde kullanmanız gerekiyorsa, tekerleği yeniden icat etmeyin ve bunlardan bir paket seçin - diğer kişiler tarafından belgelenip desteklenir.
Daerdemandt

12

ES6 Tek Çizgi Düzleştir

Bkz lodash flatten , alt çizgi düzleştirmek (sığ true)

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

veya

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

İle test edildi

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 Tek Çizgi Derin Düzleştir

Bkz lodash flattenDeep , çizgi düzleştirmek

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

İle test edildi

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. örneğiniz daha iyi yazılmıştır, Array.prototype.concat.apply([], arr)çünkü sadece concatişleve ulaşmak için ekstra bir dizi oluşturursunuz . Çalışma zamanları, çalıştırdıklarında optimize edebilir veya etmeyebilir, ancak prototip üzerindeki işleve erişmek, zaten her durumda olduğundan daha çirkin görünmüyor.
Mörre

11

İç içe dizinin herhangi bir derinliği için Array.flat()ile kullanabilirsiniz 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)

tarayıcı uyumluluğu için buraya bakın


8

Haskellesque yaklaşımı

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 ile kabul edin. Bu iplikteki en zarif çözümü eller.
Nicolás Fantone

8

ES6 yolu:

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-yuvalanmış diziler için ES3 yedekli işlev için ES5 yolu :

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

Yalnızca 1 dize öğesine sahip dizileriniz varsa:

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

işi yapacak. Bt özellikle kod örneğinizle eşleşir.


3
Aşağı kim oy verdi, lütfen nedenini açıklayın. İyi bir çözüm arıyordum ve bunu en çok sevdiğim tüm çözümlerden.
İsimsiz

2
@Anonymous teknik olarak sorunun gereksinimlerini karşıladığından aşağı indirmedim, ancak bunun genel durumda yararlı olmayan oldukça zayıf bir çözüm olması muhtemeldir. Burada kaç tane daha iyi çözüm olduğunu göz önünde bulundurursak, birden fazla elemanınız olduğu anda ya da dize olmadığı zaman birinin bunu yapmasını tavsiye etmem.
Thor84no

2
Bu çözümü seviyorum =)
Huei Tan

2
Sadece dizeleri 1 dize elemanı ile işlemez, aynı zamanda bu diziyi de işler ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
alucic

Bu yöntemi kendi başıma keşfettim, ancak zaten bir yerde belgelenmesi gerektiğini biliyordum, aramam burada sona erdi. Bu çözüm ile dezavantajı, denemek, dizelere numaraları, boolean vb coerces edilir [1,4, [45, 't', ['e3', 6]]].toString().split(',') ----- ---- veya [1,4, [45, 't', ['e3', 6], false]].toString().split(',')
Sudhansu Choudhary

7
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

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

(@Danhbear'ın yorumuna dayanarak bunu ayrı bir cevap olarak yazıyorum.)


7

Yerden tasarruf sağlayan bir jeneratör fonksiyonu öneriyorum :

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

İstenirse, aşağıdaki gibi düzleştirilmiş değerler dizisi oluşturun:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]

Bu yaklaşımı seviyorum. Stackoverflow.com/a/35073573/1175496'ya benzer , ancak ...jeneratörde yineleme yapmak için forma operatörünü kullanır .
Kırmızı Bezelye

6

Ben bütün dizeyi, olduğu gibi, bir dizeye dönüştürmek istiyorum, ama diğer cevapların aksine, bunu kullanarak JSON.stringifyve toString()istenmeyen bir sonuç üreten yöntemi, kullanmaz .

Bu JSON.stringifyçıktı ile geriye kalan tüm parantezleri kaldırmak, sonucu yine başlangıç ​​ve bitiş parantezleri ile sarmak JSON.parseve ipi "hayata" döndüren sonucu sunmaktır.

  • Sonsuz iç içe dizileri herhangi bir hız maliyeti olmadan işleyebilir.
  • Virgül içeren dizeler olan Dizi öğelerini doğru şekilde işleyebilir.

var arr = ["abc",[[[6]]],["3,4"],"2"];

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

console.log(flattened)

  • Yalnızca çok boyutlu Dizeler / Sayılar Dizisi için (Nesneler değil)

Çözümünüz yanlış. Bu ["345", "2", "3,4", "2"]değerlerin her birini ayrı endekslere ayırmak yerine iç dizileri düzleştirirken virgül içerecektir
pizzarob

@realseanp - bu Dizi öğesindeki değeri yanlış anladınız. Çözümümün gücünü diğerlerinden daha fazla vurgulamak için bu virgülü Array sınırlayıcı virgül olarak değil, bir değer olarak koydum "3,4".
vsync

1
Yanlış
anladım

bu kesinlikle bunun için gördüğüm en hızlı çözüm gibi görünüyor; @vsync'in herhangi bir tuzağı olduğunu biliyor musunuz (tabii ki biraz acayip görünüyor - iç içe dizilere dize gibi davranmak: D)
George Katsanos

@GeorgeKatsanos - Bu yöntem, İlkel değere sahip olmayan dizi öğeleri (ve iç içe öğeler), örneğin bir DOM öğesine işaret eden bir öğe için çalışmaz
vsync 27:17

6

Ayrıca yeni Array.Flat()yöntemi de deneyebilirsiniz . Aşağıdaki şekilde çalışır:

let arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]].flat()

console.log(arr);

flat()Yöntem derinliği 1 tabakaya ardışık kadar içine birleştirilmiş tüm alt dizi elemanları ile yeni bir dizi oluşturur (diziler içine, yani diziler)

3 boyutlu veya daha yüksek boyutlu dizileri de düzleştirmek istiyorsanız, düz yöntemi birden çok kez çağırmanız yeterlidir. Örneğin (3 boyut):

let arr = [1,2,[3,4,[5,6]]].flat().flat().flat();

console.log(arr);

Dikkatli ol!

Array.Flat()yöntem nispeten yenidir. İe gibi daha eski tarayıcılar yöntemi uygulamamış olabilir. Kodun tüm tarayıcılarda çalışmasını istiyorsanız, JS'nizi daha eski bir sürüme aktarmanız gerekebilir. Geçerli tarayıcı uyumluluğu için MD web belgelerini kontrol edin.


2
daha yüksek boyutlu dizileri düzleştirmek için flat yöntemini Infinitybağımsız değişkenle çağırabilirsiniz . arr.flat(Infinity)
Mikhail

Bu çok güzel bir ek, teşekkürler Mikhail!
Willem van der Veen

6

Forma operatörünü kullanma:

const input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
const output = [].concat(...input);
console.log(output); // --> ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]


5

Bu zor değil, sadece dizileri yineleyin ve birleştirin:

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

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

5

Bu, RECURSION için bir iş gibi görünüyor!

  • Birden çok iç içe yerleştirme düzeyini yönetir
  • Boş dizileri ve dizi olmayan parametreleri yönetir
  • Mutasyon yok
  • Modern tarayıcı özelliklerine güvenmez

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

Kullanımı:

flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7] 

dikkatli, flatten(new Array(15000).fill([1]))atar Uncaught RangeError: Maximum call stack size exceededve benim devTools 10 saniye
dondurdu

@pietrovismara, testim 1s civarında.
Ivan Yan

5

Özyineleme ve kapanışları kullanarak yaptım

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
Basit ve tatlı, bu cevap kabul edilen cevaptan daha iyi çalışır. Sadece ilk seviyeye değil, derin yuvalanmış seviyelere yassılaşır
Om Shankar

1
Sözcüksel kapsam belirleme ve bir kapatma değil AFAIK
dashambles

@dashambles doğrudur - fark, eğer bir kapatma olsaydı, iç işlevi dışarıya geri döndürürdünüz ve dış işlev bittiğinde, iç işlevi işlevine erişmek için kullanmaya devam edebilirsiniz. Burada dış fonksiyonun ömrü iç fonksiyonun ömründen daha uzundur, bu yüzden asla bir "kapanma" yaratılmaz.
Mörre

5

Geçen gün ES6 Jeneratörleri ile dalga geçiyordum ve bu özü yazdım . Hangi içerir ...

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

Temelde, orijinal diziler üzerinde döngü yapan bir jeneratör oluşturuyorum, eğer bir dizi bulursa , dahili dizileri sürekli olarak düzleştirmek için özyineleme ile birlikte verim * operatörünü kullanır . Öğe bir dizi değilse, yalnızca tek bir öğe verir . Sonra ES6 Spread operatörünü (splat operatörü olarak da bilinir) kullanarak jeneratörü yeni bir dizi örneğine yassılaştırdım.

Bunun performansını test etmedim, ancak jeneratörleri ve verim * operatörünü kullanmanın güzel basit bir örneği olduğunu düşünüyorum.

Ama yine de, sadece aptalca yapıyordum, bu yüzden bunu yapmanın daha performanslı yolları olduğundan eminim.


1
Benzer cevap (jeneratör ve getiri delegasyonunu kullanarak), ancak PHP formatında
Red Pea

5

sadakatsiz en iyi çözüm

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.