ผสาน / เรียบอาร์เรย์ของอาร์เรย์


1104

ฉันมีอาร์เรย์ JavaScript เช่น:

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

ฉันจะไปเกี่ยวกับการรวมอาร์เรย์ภายในแยกกันเป็นหนึ่งเช่น:

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

gist.github.com/Nishchit14/4c6a7349b3c778f7f97b912629a9f228ลิงก์นี้จะอธิบาย ES5 & ES6 แบน
Nishchit Dhanani

17
โซลูชันทั้งหมดที่ใช้reduce+ concatคือ O ((N ^ 2) / 2) ซึ่งเป็นคำตอบที่ยอมรับได้ (เพียงหนึ่งสายที่โทรไปconcat) จะมากที่สุด O (N * 2) บนเบราว์เซอร์ที่ไม่ดีและ O (N) บน สิ่งที่ดี. นอกจากนี้ยังมีวิธีการแก้ปัญหา Denys เหมาะสำหรับคำถามที่เกิดขึ้นจริงและไม่เกิน 2 concatเท่าเร็วกว่าเดียว สำหรับreduceคนที่สนุกกับการเขียนโค้ดเล็ก ๆ น้อย ๆ แต่ตัวอย่างเช่นถ้าอาร์เรย์มีองค์ประกอบย่อย 1,000 รายการย่อยหนึ่งโซลูชั่นลด + concat ทั้งหมดจะทำการ500500 การดำเนินการโดยที่การเชื่อมโยงเดี่ยวหรือการวนรอบง่ายจะดำเนินการ 1,000 ครั้ง
gman

12
ผู้ประกอบการแพร่กระจายเพียงแค่ผู้ใช้[].concat(...array)
Oleg Dater

@gman ลด + concat solution อย่างไร O ((N ^ 2) / 2)? stackoverflow.com/questions/52752666/…พูดถึงความซับซ้อนที่แตกต่าง
Usman

4
กับเบราว์เซอร์ใหม่ล่าสุดที่สนับสนุนES2019 : array.flat(Infinity)ที่Infinityความลึกสูงสุดให้เรียบ
Timothy Gu

คำตอบ:


1860

คุณสามารถใช้concatเพื่อรวมอาร์เรย์:

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

console.log(merged);

การใช้applyวิธีการconcatจะใช้พารามิเตอร์ตัวที่สองเป็นอาร์เรย์ดังนั้นบรรทัดสุดท้ายจะเหมือนกับสิ่งนี้:

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

นอกจากนี้ยังมีArray.prototype.flat()วิธีการ (แนะนำใน ES2019) ซึ่งคุณสามารถใช้เพื่อแผ่อาร์เรย์แม้ว่ามันจะใช้ได้เฉพาะใน Node.js เริ่มต้นด้วยรุ่นที่ 11 และไม่ได้ทั้งหมดใน Internet Explorer

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
โปรดทราบว่าconcatไม่ได้ปรับเปลี่ยนอาร์เรย์แหล่งที่มาเพื่อให้อาร์เรย์จะยังคงว่างเปล่าหลังจากการเรียกร้องให้merged concatดีกว่าที่จะพูดบางอย่างเช่น:merged = merged.concat.apply(merged, arrays);
เนท

65
var merged = [].concat.apply([], arrays);ดูเหมือนว่าจะทำงานได้ดีเพื่อให้ได้ในบรรทัดเดียว แก้ไข: ตามคำตอบของ Nikita แสดงให้เห็นแล้ว
Sean

44
Array.prototype.concat.apply([], arrays)หรือ
danhbear

30
หมายเหตุ: คำตอบนี้แผ่ระดับลึกเพียงหนึ่งระดับเท่านั้น สำหรับการแบนซ้ำเรียกดูคำตอบโดย @Trindaz
Phrogz

209
นอกจากความคิดเห็นของ @ Sean: ไวยากรณ์ ES6 ทำให้มีความรัดกุมมาก:var merged = [].concat(...arrays)
Sethi

505

นี่คือฟังก์ชั่นสั้น ๆ ที่ใช้วิธีการอาร์เรย์ JavaScript ที่ใหม่กว่าบางส่วนเพื่อทำให้อาเรย์ n-แผ่แบน

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

การใช้งาน:

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
ฉันชอบวิธีนี้ เป็นเรื่องทั่วไปมากขึ้นและรองรับอาร์เรย์ที่ซ้อนกัน
alfredocambera

10
โปรไฟล์การใช้งานหน่วยความจำสำหรับโซลูชันนี้คืออะไร ดูเหมือนว่ามันจะสร้างอาร์เรย์กลางจำนวนมากในระหว่างการเรียกซ้ำหาง ....
JBRWilkinson

7
@ayjay เป็นค่าเริ่มต้นของสะสมสำหรับฟังก์ชันลดสิ่งที่mdnเรียกค่าเริ่มต้น ในกรณีนี้มันเป็นค่าของในสายแรกที่ฟังก์ชั่นที่ไม่ระบุชื่อส่งผ่านไปยังflat reduceหากไม่ได้ระบุไว้การเรียกใช้ครั้งแรกเพื่อreduceผูกค่าแรกจากอาร์เรย์ถึงflatซึ่งในที่สุดจะส่งผลให้1ถูกผูกไว้กับflatทั้งสองตัวอย่าง 1.concatไม่ใช่ฟังก์ชั่น
Noah Freitas

19
หรือในรูปแบบที่สั้นกว่าและเซ็กซี่กว่า: const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
Tsvetomir Tsonev

12
riffing ใน @TsvetomirTsonev และโนอาห์โซลูชั่นสำหรับการทำรังโดยพล:const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);
Will

314

มีวิธีการที่ซ่อนอยู่อย่างสับสนซึ่งสร้างอาเรย์ใหม่โดยไม่เปลี่ยนแปลงมิวเตชันดั้งเดิม:

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


11
ใน CoffeeScript นี่คือ[].concat([[1],[2,3],[4]]...)
amoebe

8
@ amoebe คำตอบของคุณให้[[1],[2,3],[4]]เป็นผล โซลูชันที่ @Nikita ให้นั้นถูกต้องสำหรับ CoffeeScript เช่นเดียวกับ JS
Ryan Kennedy

5
อ่าฉันพบข้อผิดพลาดของคุณ [].concat([1],[2,3],[4],...)คุณมีคู่พิเศษของวงเล็บในสัญกรณ์ของคุณควรจะเป็น
Ryan Kennedy

21
ฉันคิดว่าฉันพบข้อผิดพลาดของคุณเช่นกัน ...มีรหัสที่เกิดขึ้นจริงไม่ได้เป็นบางจุดจุดไข่ปลา
amoebe

3
การใช้ฟังก์ชั่นห้องสมุดไม่ได้หมายความว่าจะมี "ความยุ่งเหยิง" ที่จำเป็นน้อยลง ความยุ่งเหยิงนั้นซ่อนอยู่ในห้องสมุด เห็นได้ชัดว่าการใช้ไลบรารี่นั้นดีกว่าการม้วนฟังก์ชั่นของคุณเอง แต่การอ้างว่าเทคนิคนี้ช่วยลด
paldepind

204

สามารถทำได้ดีที่สุดโดยใช้จาวาสคริปต์ลดฟังก์ชั่น

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

หรือด้วย ES2015:

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

JS-ซอ

เอกสาร Mozilla


1
@JohnS ลดค่าฟีดจริงลงบน concat ค่าเดียว (อาร์เรย์) ต่อเทิร์น หลักฐาน? นำลด () และดูว่าใช้งานได้หรือไม่ ลด () ที่นี่เป็นทางเลือกแทน Function.prototype.apply ()
André Werlang

3
ฉันจะใช้การลดเช่นกัน แต่สิ่งหนึ่งที่น่าสนใจที่ฉันเรียนรู้จากตัวอย่างนี้คือส่วนใหญ่คุณไม่จำเป็นต้องผ่านค่าเริ่มต้น :)
José F. Romaniello

2
ปัญหาของการลดคืออาร์เรย์ต้องไม่ว่างเปล่าดังนั้นคุณจะต้องเพิ่มการตรวจสอบเพิ่มเติม
calbertts

4
@calbertts เพียงแค่ผ่านค่าเริ่มต้น[]และไม่จำเป็นต้องมีการตรวจสอบเพิ่มเติม

8
เนื่องจากคุณใช้ ES6 คุณสามารถใช้ตัวดำเนินการสเปรดเป็นตัวอักษรอาร์เรย์ได้ arrays.reduce((flatten, arr) => [...flatten, ...arr])
Putzi San

109

มีวิธีการเนทีฟแบบใหม่ที่เรียกว่าflatเพื่อทำสิ่งนี้

(ราวปลายปี 2562 flatปัจจุบันได้รับการตีพิมพ์ในมาตรฐาน ECMA 2019 และcore-js@3(ห้องสมุดของ babel) รวมไว้ในห้องสมุด polyfill ของพวกเขา)

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
มันเป็นความอัปยศที่นี่ไม่ใช่แม้แต่ในหน้าแรกของคำตอบ คุณลักษณะนี้มีให้ใน Chrome 69 และ Firefox 62 (และ Node 11 สำหรับผู้ที่ทำงานในแบ็กเอนด์)
Matt M.


2
-1; Nope นี้ไม่ได้เป็นส่วนหนึ่งของECMAScript 2018 ยังคงเป็นเพียงข้อเสนอที่ไม่ได้ทำกับข้อกำหนด ECMAScript ใด ๆ
Mark Amery

1
ฉันคิดว่าตอนนี้เราสามารถพิจารณาเรื่องนี้ได้แล้วเพราะตอนนี้มันเป็นส่วนหนึ่งของมาตรฐาน (2019) .. เราจะทบทวนส่วนการแสดงอีกครั้งได้ไหม?
Yuvaraj

ดูเหมือนว่ายังไม่ได้รับการสนับสนุนจากเบราว์เซอร์ Microsoft ใด ๆ (อย่างน้อยตอนที่ฉันเขียนความคิดเห็นนี้)
Laurent S.

78

คำตอบส่วนใหญ่ที่นี่ไม่ทำงานในอาร์เรย์ขนาดใหญ่ (เช่น 200,000 องค์ประกอบ) และแม้ว่าพวกเขาจะทำพวกเขาจะช้า คำตอบของ polkovnikov.ph นั้นมีประสิทธิภาพดีที่สุด แต่ก็ไม่ได้ผลสำหรับการแบนอย่างลึก

นี่คือทางออกที่เร็วที่สุดซึ่งทำงานบนอาร์เรย์ด้วยการซ้อนหลายระดับ :

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

ตัวอย่าง

อาร์เรย์ขนาดใหญ่

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

มันจัดการกับอาร์เรย์ขนาดใหญ่ได้ดี ในเครื่องของฉันรหัสนี้ใช้เวลาประมาณ 14 ms ในการดำเนินการ

อาร์เรย์ที่ซ้อนกัน

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

มันทำงานร่วมกับอาร์เรย์ที่ซ้อนกัน [1, 1, 1, 1, 1, 1, 1, 1]รหัสนี้ผลิต

อาร์เรย์ที่มีระดับซ้อนกันต่างกัน

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

มันไม่มีปัญหาใด ๆ กับอาร์เรย์ที่แบนเหมือนอันนี้


ยกเว้นชุดใหญ่ของคุณจะค่อนข้างแบน วิธีนี้จะไม่ทำงานสำหรับอาร์เรย์ที่ซ้อนกันอย่างลึกล้ำ ไม่มีวิธีแก้ปัญหาแบบเรียกซ้ำ อันที่จริงไม่มีเบราว์เซอร์ แต่ Safari มี TCO ในขณะนี้ดังนั้นอัลกอริทึมแบบเรียกซ้ำจะทำงานได้ไม่ดี
nitely

@nitely แต่ในสถานการณ์จริงคุณจะมีอาร์เรย์ที่มีการซ้อนมากกว่าสองสามระดับในสถานการณ์ใดบ้าง
MichałPerłakowski

2
โดยทั่วไปเมื่อสร้างอาร์เรย์จากเนื้อหาที่ผู้ใช้สร้างขึ้น
nitely

@ 0xcaff ใน Chrome ไม่สามารถใช้งานได้กับอาร์เรย์ 20,000 องค์ประกอบ (คุณจะได้รับRangeError: Maximum call stack size exceeded) สำหรับอาร์เรย์ 20,000 องค์ประกอบจะใช้เวลา 2-5 มิลลิวินาที
MichałPerłakowski

3
ความซับซ้อนของสัญกรณ์ O คืออะไร?
George Katsanos

58

ปรับปรุง: มันกลับกลายเป็นว่าโซลูชั่นนี้ไม่ทำงานกับอาร์เรย์ขนาดใหญ่ คุณกำลังมองหาทางออกที่ดีกว่าและเร็วกว่าลองดูคำตอบนี้


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

เป็นเพียงการขยายarrและส่งผ่านเป็นอาร์กิวเมนต์ไปยังconcat()ซึ่งรวมอาร์เรย์ทั้งหมดเป็นหนึ่ง [].concat.apply([], arr)มันเทียบเท่ากับ

คุณสามารถลองทำสิ่งนี้เพื่อให้แบนราบ:

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

การอ้างอิงสำหรับองค์ประกอบ ECMAScript 6 ที่ใช้ในคำตอบนี้:


หมายเหตุ: find()เบราว์เซอร์ทั้งหมดไม่รองรับฟังก์ชั่นวิธีการและลูกศร แต่ไม่ได้หมายความว่าคุณไม่สามารถใช้คุณสมบัติเหล่านี้ได้ในขณะนี้ เพียงใช้Babel - มันแปลงรหัส ES6 เป็น ES5


เนื่องจากคำตอบเกือบทั้งหมดในที่นี้ใช้applyในทางที่ผิดฉันจึงลบความคิดเห็นของฉันออกจากคุณ ฉันยังคงคิดว่าการใช้apply/ การแพร่กระจายด้วยวิธีนี้เป็นคำแนะนำที่ไม่ดี แต่เนื่องจากไม่มีใครสนใจ ...

@ LUH3417 มันไม่ได้เป็นเช่นนั้นฉันขอขอบคุณความคิดเห็นของคุณ มันกลับกลายเป็นว่าคุณพูดถูก - โซลูชั่นนี้ใช้ไม่ได้กับอาร์เรย์ขนาดใหญ่ ฉันโพสต์คำตอบอื่นที่ใช้งานได้ดีแม้จะมีอาร์เรย์ 200,000 รายการ
MichałPerłakowski

หากคุณกำลังใช้ ES6 คุณสามารถลดไปที่:const flatten = arr => [].concat(...arr)
Eruant

คุณหมายถึงอะไร "ไม่ทำงานกับอาร์เรย์ขนาดใหญ่"? ใหญ่แค่ไหน เกิดอะไรขึ้น?
GEMI

4
@GEMI ตัวอย่างเช่นพยายามทำให้อาเรย์ขององค์ประกอบ 500,000 รายการโดยใช้วิธีนี้ให้ "RangeError: เกินขนาดสแต็กการโทรสูงสุด"
MichałPerłakowski

51

คุณสามารถใช้ขีดล่าง :

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

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

20
อ๊ะมีคนเพิ่ม Perl ใน JS ไหม :-)
JBRWilkinson

9
@JBRWilkinson JS อาจต้องการเรียนรู้คุณ Haskell สำหรับ Great Good !?
สไตล์

1+ - นอกจากนี้คุณยังสามารถระบุได้ว่าคุณต้องการตื้นบี้อาร์เรย์โดยระบุtrueสำหรับอาร์กิวเมนต์ที่สอง
Josh Crozier

7
เพื่อความสมบูรณ์เกี่ยวกับความคิดเห็นของ @ styfle: learnyouahaskell.com
Simon A. Eugster

41

ขั้นตอนทั่วไปหมายความว่าเราไม่จำเป็นต้องเขียนความซับซ้อนใหม่ทุกครั้งที่เราจำเป็นต้องใช้พฤติกรรมที่เฉพาะเจาะจง

concatMap(หรือflatMap) เป็นสิ่งที่เราต้องการในสถานการณ์นี้

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

ความสุขุม

และใช่คุณเดาได้อย่างถูกต้องก็เพียง flattens หนึ่งระดับซึ่งเป็นว่าวิธีการที่มันควรจะทำงาน

ลองนึกภาพชุดข้อมูลบางอย่างเช่นนี้

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

ตกลงตอนนี้เราต้องการพิมพ์บัญชีรายชื่อที่แสดงผู้เล่นทุกคนที่จะเข้าร่วมgame...

const gamePlayers = game =>
  flatten (game)

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

หากflattenโพรซีเดอร์ของเราแบนอาเรย์ซ้อนกันเราก็จะพบกับผลลัพธ์ขยะ ...

const gamePlayers = game =>
  badGenericFlatten(game)

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

ลูกน้อยของโรลลี่

นั่นไม่ได้หมายความว่าบางครั้งคุณไม่ต้องการที่จะแผ่อาร์เรย์ที่ซ้อนกันออกไปเช่นกัน - เฉพาะที่ไม่ควรเป็นพฤติกรรมเริ่มต้น

เราสามารถทำdeepFlattenขั้นตอนได้อย่างง่ายดาย ...

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

ที่นั่น ตอนนี้คุณมีเครื่องมือสำหรับงานแต่ละงาน - หนึ่งสำหรับฉับระดับหนึ่งในการทำรังและสำหรับกำจัดรังทั้งหมดflattendeepFlatten

บางทีคุณอาจจะเรียกมันobliterateหรือถ้าคุณไม่ชอบชื่อnukedeepFlatten


อย่าวนซ้ำสองครั้ง!

แน่นอนว่าการใช้งานด้านบนนั้นฉลาดและรัดกุม แต่การใช้งาน.mapตามด้วยการเรียกเพื่อ.reduceหมายความว่าเรากำลังทำซ้ำมากกว่าที่จำเป็น

การใช้คอมบิเนเตอร์ที่ไว้ใจได้ที่ฉันเรียกmapReduceจะช่วยให้การทำซ้ำลดลง มันต้องใช้ฟังก์ชั่นการทำแผนที่m :: a -> b, ฟังก์ชั่นการลดr :: (b,a) ->bและส่งกลับฟังก์ชั่นการลดใหม่ - Combinator นี้เป็นหัวใจของก้อน ; หากคุณสนใจฉันได้เขียนคำตอบอื่น ๆ เกี่ยวกับพวกเขา

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


บ่อยครั้งที่ฉันเห็นคำตอบของคุณฉันต้องการถอนฉันออกเพราะพวกเขาไร้ค่า คำตอบที่ดี! concatตัวเองไม่ได้ระเบิดกองเท่านั้น...และapplyไม่ (พร้อมกับอาร์เรย์ที่มีขนาดใหญ่มาก) ฉันไม่เห็นมัน ตอนนี้ฉันรู้สึกแย่มาก

@ LUH3417 คุณไม่ควรรู้สึกแย่ คุณยังให้คำตอบที่ดีพร้อมคำอธิบายที่เป็นลายลักษณ์อักษร ทุกสิ่งที่นี่เป็นประสบการณ์การเรียนรู้ - ฉันมักจะทำผิดพลาดและเขียนคำตอบที่ไม่ดีเช่นกัน ฉันกลับมาเยี่ยมพวกเขาอีกครั้งในสองสามเดือนและคิดว่า "ฉันกำลังคิดอยู่หรือไม่?" และจบลงด้วยการแก้ไขสิ่งต่าง ๆ อย่างสมบูรณ์ ไม่มีคำตอบที่สมบูรณ์แบบ เราทุกคนอยู่ในช่วงการเรียนรู้ ^ _ ^
ขอบคุณ

1
โปรดทราบว่าconcatใน Javascript มีความหมายแตกต่างจากใน Haskell Haskell's concat( [[a]] -> [a]) จะถูกเรียกflattenใน Javascript และมีการใช้งานเป็นfoldr (++) [](Javascript: foldr(concat) ([])สมมติว่าฟังก์ชั่น curried) จาวาสคริปต์ที่concatเป็นผนวกแปลก ( (++)ใน Haskell) ซึ่งสามารถจัดการทั้งและ[a] -> [a] -> [a] a -> [a] -> [a]

ฉันคิดว่าชื่อที่ดีกว่าคือflatMapเพราะนั่นคือสิ่งที่แน่นอนconcatMap: bindตัวอย่างของlistmonad จะดำเนินการเป็นconcatpMap foldr ((++) . f) []แปลเป็น const flatMap = f => foldr(comp(concat) (f)) ([])Javascript: compนี้เป็นหลักสูตรที่คล้ายกับการดำเนินงานของคุณโดยไม่

ความซับซ้อนของอัลกอริทึมนั้นคืออะไร?
George Katsanos

32

วิธีแก้ปัญหาสำหรับกรณีทั่วไปมากขึ้นเมื่อคุณอาจมีองค์ประกอบที่ไม่ใช่อาร์เรย์ในอาร์เรย์ของคุณ

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
วิธีการนี้มีประสิทธิภาพมากในการแฟบรูปแบบอาร์เรย์ที่ซ้อนกันของผลชุดที่คุณได้รับจากแบบสอบถาม JsonPath
kevinjansz

1
เพิ่มเป็นวิธีการของอาร์เรย์: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

สิ่งนี้จะพังถ้าเราผ่านไปในอาร์กิวเมนต์ที่สองด้วยตนเอง ตัวอย่างเช่นลองนี้: flattenArrayOfArrays (arr, 10)หรือนี่flattenArrayOfArrays(arr, [1,[3]]);- อาร์กิวเมนต์ที่สองเหล่านั้นจะถูกเพิ่มไปยังเอาต์พุต
Om Shankar

2
คำตอบนี้ไม่สมบูรณ์! ผลลัพธ์ของการสอบถามซ้ำไม่เคยถูกกำหนดไว้ที่ใดดังนั้นผลลัพธ์ของการสอบถามซ้ำจะหายไป
r3wt

1
@ r3wt ไปข้างหน้าและเพิ่มการแก้ไข สิ่งที่คุณต้องทำคือตรวจสอบให้แน่ใจว่าอาร์เรย์ปัจจุบันที่คุณกำลังบำรุงรักษาrจริงจะเชื่อมผลลัพธ์จากการสอบถามซ้ำ
สิงหาคม

29

reduce(callback[, initialValue])วิธีการเกี่ยวกับการใช้JavaScript 1.8

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

อยากทำงาน


8
[[1], [2,3]].reduce( (a,b) => a.concat(b), [] )เซ็กซี่มากขึ้น
golopot

5
ไม่ต้องการอาร์กิวเมนต์ที่สองในกรณีของเราง่าย ๆ[[1], [2,3]].reduce( (a,b) => a.concat(b))
Umair Ahmed

29

โซลูชัน ECMAScript 6 อื่นในรูปแบบการทำงาน:

ประกาศฟังก์ชั่น:

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

และใช้มัน:

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

พิจารณาฟังก์ชั่นพื้นเมืองArray.prototype.flat () (ข้อเสนอสำหรับ ES6) ที่มีอยู่ในเบราว์เซอร์รุ่นใหม่ล่าสุด ขอบคุณ @ (КонстантинВан) และ @ (Mark Amery) พูดถึงมันในความคิดเห็น

flatฟังก์ชั่นมีพารามิเตอร์หนึ่งระบุความลึกที่คาดหวังของอาร์เรย์รังซึ่งเท่ากับ1เป็นค่าเริ่มต้น

[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
นี่เป็นสิ่งที่ดีและเรียบร้อย แต่ฉันคิดว่าคุณได้ใช้ยาเกินขนาด ES6 แล้ว ไม่จำเป็นต้องใช้ฟังก์ชั่นด้านนอกเพื่อเป็นฟังก์ชั่นลูกศร ฉันจะติดกับฟังก์ชั่นลูกศรเพื่อลดการโทรกลับ แต่ควรทำให้ตัวแบนเรียบเป็นฟังก์ชันปกติ
Stephen Simpson

3
@StephenSimpson แต่มีความจำเป็นสำหรับฟังก์ชั่นด้านนอกที่จะไม่ฟังก์ชั่นที่แคบ? "แผ่ตัวเองควรจะเป็นฟังก์ชั่นปกติ" - โดย "ปกติ" คุณหมายถึง "ไม่ใช่ลูกศร" แต่ทำไม? เหตุใดจึงใช้ฟังก์ชั่นลูกศรในการโทรเพื่อลดขนาด? คุณสามารถระบุเหตุผลของคุณได้หรือไม่?
ขอบคุณ

@naomik เหตุผลของฉันคือมันไม่จำเป็น มันเป็นเรื่องของสไตล์ ฉันควรจะชัดเจนมากขึ้นในความคิดเห็นของฉัน ไม่มีเหตุผลการเข้ารหัสที่สำคัญในการใช้อย่างใดอย่างหนึ่ง อย่างไรก็ตามฟังก์ชั่นนี้ง่ายต่อการดูและอ่านในรูปแบบที่ไม่ใช่ลูกศร ฟังก์ชั่นด้านในมีประโยชน์ในฐานะฟังก์ชั่นลูกศรเนื่องจากมีขนาดเล็กกว่า (และไม่มีการสร้างบริบท) ฟังก์ชั่น Arrow เหมาะสำหรับการสร้างฟังก์ชั่นการอ่านที่กะทัดรัดและหลีกเลี่ยงความสับสน อย่างไรก็ตามพวกเขาสามารถทำให้ยากต่อการอ่านเมื่อลูกศรที่ไม่ใช่ลูกศรเพียงพอ คนอื่นอาจไม่เห็นด้วยแม้ว่า!
Stephen Simpson

ได้รับRangeError: Maximum call stack size exceeded
แมตต์เวสต์เลก

@ Matt โปรดแบ่งปัน evnironment ที่คุณใช้ในการทำให้เกิดข้อผิดพลาดอีกครั้ง
diziaq

28

หากต้องการจัดเรียงอาร์เรย์ของอาร์เรย์องค์ประกอบเดียวให้เรียบคุณไม่จำเป็นต้องนำเข้าไลบรารีการวนรอบแบบเรียบง่ายเป็นโซลูชันที่ง่ายและมีประสิทธิภาพมากที่สุด :

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

สำหรับผู้ลงคะแนนเสียง: โปรดอ่านคำถามอย่าลงคะแนนเพราะมันไม่เหมาะกับปัญหาที่แตกต่างของคุณ วิธีนี้เป็นวิธีที่เร็วและง่ายที่สุดสำหรับคำถามที่ถาม


4
ฉันจะพูดว่า "อย่ามองสิ่งที่เป็นความลับอีกต่อไป" ^^

4
ฉันหมายถึง ... เมื่อฉันเห็นคนแนะนำให้ใช้ห้องสมุดเพื่อที่ ... มันบ้า ...
Denys Séguret

3
อ่าถ้าจะใช้ห้องสมุดแค่นี้มันคงจะงี่เง่า ... แต่ถ้ามันมีอยู่แล้ว

9
@dystroy ส่วนที่มีเงื่อนไขของ for loop นั้นไม่สามารถอ่านได้ หรือเป็นแค่ฉัน: D
Andreas

4
มันไม่สำคัญเลยว่ามันจะเป็นความลับได้อย่างไร รหัสนี้ "แบน" นี้จะ['foo', ['bar']] ['f', 'bar']
jonschlinkert

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

นี่คือวิธีที่ฉันทำมันตลอดทั้งรหัสของฉัน แต่ฉันไม่แน่ใจว่าความเร็วเปรียบเทียบกับคำตอบที่ยอมรับได้อย่างไรถ้าคุณมีอาร์เรย์ที่ยาวมาก
Michael Aaron Wilson

14

โปรดทราบ:เมื่อใช้Function.prototype.apply( [].concat.apply([], arrays)) หรือตัวดำเนินการสเปรด ( [].concat(...arrays)) เพื่อทำให้แบนอาร์เรย์ทั้งคู่สามารถทำให้เกิดการโอเวอร์โฟลว์ของสแต็กขนาดใหญ่สำหรับอาร์เรย์ขนาดใหญ่

นี่คือการใช้งานแบบกองซ้อนที่ปลอดภัยในรูปแบบการใช้งานซึ่งชั่งน้ำหนักความต้องการที่สำคัญที่สุดต่อกัน:

  • สามารถนำมาใช้
  • การอ่าน
  • ความรัดกุม
  • ประสิทธิภาพ

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

ทันทีที่คุณคุ้นเคยกับฟังก์ชั่นลูกศรขนาดเล็กในรูปแบบ curried องค์ประกอบของฟังก์ชั่นและฟังก์ชั่นการสั่งซื้อที่สูงขึ้นรหัสนี้จะอ่านออกมาเป็นร้อยแก้ว การเขียนโปรแกรมนั้นประกอบไปด้วยการสร้างบล็อคเล็ก ๆ ที่มักจะทำงานได้ตามที่คาดไว้เพราะมันไม่มีผลข้างเคียงใด ๆ


1
ฮ่าฮ่า เคารพคำตอบของคุณโดยสิ้นเชิงแม้ว่าการอ่านการเขียนโปรแกรมฟังก์ชั่นเช่นนี้ยังคงเหมือนกับการอ่านตัวอักษรญี่ปุ่นโดยตัวละครกับฉัน (พูดภาษาอังกฤษ)
Tarwin Stroh-Spijer

2
หากคุณพบว่าตัวเองใช้คุณสมบัติของภาษา A ในภาษา B ไม่ใช่ส่วนหนึ่งของโครงการโดยมีเป้าหมายเพียงอย่างเดียวในการทำสิ่งนี้แล้วมีบางคนที่ทำผิดพลาด เป็นคุณไหม เพียงไปกับการconst flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);ช่วยให้คุณประหยัดขยะภาพและคำอธิบายกับเพื่อนร่วมทีมของคุณว่าทำไมคุณต้องมีฟังก์ชั่นพิเศษ 3 และบางฟังก์ชั่นการโทร
Daerdemandt

3
@Daerdemandt แต่ถ้าคุณเขียนมันเป็นฟังก์ชั่นแยกคุณอาจจะสามารถนำมันกลับมาใช้ในรหัสอื่นได้
MichałPerłakowski

@ MichałPerłakowskiหากคุณจำเป็นต้องใช้มันในหลาย ๆ ที่แล้วอย่าประดิษฐ์วงล้อใหม่และเลือกแพ็คเกจจากสิ่งเหล่านี้ - บันทึกและสนับสนุนโดยคนอื่น
Daerdemandt

12

ES6 One Line Flatten

ดูที่พักแบนราบ , ขีดล่างแบน (ตื้นtrue)

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

หรือ

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

ทดสอบกับ

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

ดูที่แบนราบยื่นลึกลงไป

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

ทดสอบกับ

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

ตัวอย่างที่สองของคุณเขียนได้ดีกว่าArray.prototype.concat.apply([], arr)เนื่องจากคุณสร้างอาร์เรย์พิเศษเพื่อไปยังconcatฟังก์ชัน Runtimes อาจปรับให้เหมาะสมหรือไม่ปรับให้เหมาะสมเมื่อเรียกใช้ แต่การเข้าถึงฟังก์ชันบนต้นแบบไม่ได้ดูน่าเกลียดกว่านี้อยู่แล้วในทุกกรณี
Mörre

11

คุณสามารถใช้Array.flat()กับ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)

ตรวจสอบที่นี่เพื่อความเข้ากันได้ของเบราว์เซอร์


8

วิธีการ 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);


1
เห็นด้วยกับ @monners มอบโซลูชันที่หรูหราที่สุดในชุดข้อความทั้งหมดนี้
Nicolás Fantone

8

วิธี 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))

วิธี ES5 สำหรับflattenฟังก์ชั่นที่มีทางเลือก ES3 สำหรับอาร์เรย์ที่ซ้อนกัน N-times:

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

หากคุณมีอาร์เรย์ที่มี 1 องค์ประกอบสตริงเท่านั้น

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

จะทำงาน Bt ที่ตรงกับตัวอย่างรหัสของคุณโดยเฉพาะ


3
ใครลงคะแนนกรุณาอธิบายว่าทำไม ฉันค้นหาโซลูชันที่เหมาะสมและโซลูชันที่ฉันชอบมากที่สุด
ไม่ระบุชื่อ

2
@ ไม่ระบุชื่อฉันไม่ลงคะแนนเพราะเป็นไปตามข้อกำหนดทางเทคนิคของคำถาม แต่อาจเป็นเพราะนี่เป็นวิธีการแก้ปัญหาที่ค่อนข้างแย่ซึ่งไม่มีประโยชน์ในกรณีทั่วไป เมื่อพิจารณาถึงวิธีการแก้ปัญหาที่ดีกว่าที่นี่ฉันไม่เคยแนะนำให้ใครมากับคนนี้เพราะมันแบ่งช่วงเวลาที่คุณมีองค์ประกอบมากกว่าหนึ่งหรือเมื่อพวกเขาไม่ได้ จำกัด
Thor84no

2
ฉันชอบวิธีนี้ =)
Huei Tan

2
มันไม่ได้จัดการเพียงแค่อาร์เรย์กับ 1 องค์ประกอบสตริงมันยังจัดการอาร์เรย์นี้ ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
alucic

ฉันค้นพบวิธีการนี้ด้วยตัวเอง แต่รู้ว่าต้องมีการบันทึกไว้ที่ใดที่หนึ่งแล้วการค้นหาของฉันสิ้นสุดที่นี่ ข้อเสียเปรียบด้วยวิธีนี้คือมันรวมตัวเลขบูลีน ฯลฯ กับสตริงลอง[1,4, [45, 't', ['e3', 6]]].toString().split(',') ---- หรือ ----- [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)


7

ฉันขอแนะนำฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่ประหยัดพื้นที่:

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

หากต้องการให้สร้างอาร์เรย์ของค่าที่แบนดังนี้:

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

ฉันชอบวิธีนี้ คล้ายกับstackoverflow.com/a/35073573/1175496แต่ใช้ตัวดำเนิน...การสเปรดเพื่อวนซ้ำผ่านตัวสร้าง
ถั่วแดง

6

ฉันอยากจะเปลี่ยนทั้งอาเรย์เป็นสตริง แต่ต่างจากคำตอบอื่น ๆ จะทำเช่นนั้นโดยใช้JSON.stringifyและไม่ใช้toString()เมธอดซึ่งให้ผลลัพธ์ที่ไม่ต้องการ

ด้วยJSON.stringifyผลลัพธ์นั้นสิ่งที่เหลืออยู่คือการลบวงเล็บทั้งหมดห่อผลลัพธ์ด้วยวงเล็บเริ่มต้นและจุดสิ้นสุดอีกครั้งและแสดงผลลัพธ์JSON.parseซึ่งทำให้สตริงกลับมาเป็น "ชีวิต"

  • สามารถจัดการกับอาร์เรย์ที่ซ้อนกันไม่สิ้นสุดโดยไม่มีค่าใช้จ่ายด้านความเร็ว
  • สามารถจัดการรายการ Array ที่เป็นสตริงที่มีเครื่องหมายจุลภาคได้อย่างถูกต้อง

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

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

console.log(flattened)

  • สำหรับอาร์เรย์ / ตัวเลขหลายมิติเท่านั้น (ไม่ใช่วัตถุ)

โซลูชันของคุณไม่ถูกต้อง มันจะมีเครื่องหมายจุลภาคเมื่ออาร์เรย์ภายในแบน["345", "2", "3,4", "2"]แทนการแยกแต่ละค่าเหล่านั้นเพื่อแยกดัชนี
pizzarob

@reanalp - คุณเข้าใจผิดค่าในรายการ Array ผมจงใจใส่เครื่องหมายจุลภาคที่เป็นค่าและไม่เป็นจุลภาคคั่นอาร์เรย์จะเน้นพลังของการแก้ปัญหาของฉันเหนือคนอื่น ๆ "3,4"ทั้งหมดซึ่งจะส่งออก
vsync

1
ฉันเข้าใจผิด
pizzarob

นั่นเป็นทางออกที่เร็วที่สุดที่ฉันเคยเห็น คุณตระหนักถึงข้อผิดพลาดใด ๆ @vsync (ยกเว้นความจริงที่ว่ามันดูค่อนข้างแฮ็คแน่นอน - รักษาอาร์เรย์ที่ซ้อนกันเป็นสตริง: D)
George Katsanos

@GeorgeKatsanos - วิธีนี้จะใช้ไม่ได้กับไอเท็มอาร์เรย์ (และไอเท็มที่ซ้อนกัน) ซึ่งไม่ได้มีค่าดั้งเดิมเช่นไอเท็มที่ชี้ไปที่องค์ประกอบ DOM
vsync

6

คุณสามารถลองArray.Flat()วิธีการใหม่ได้เช่นกัน มันทำงานในลักษณะดังต่อไปนี้:

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

console.log(arr);

flat()วิธีการสร้างอาร์เรย์ใหม่กับทุกองค์ประกอบย่อยอาร์เรย์ตัดแบ่งเป็นมันซ้ำขึ้นไปที่ชั้น 1 ของความลึก (อาร์เรย์เช่นภายในอาร์เรย์)

หากคุณต้องการแผ่อาร์เรย์ 3 มิติหรือมิติที่สูงขึ้นให้เรียบคุณก็สามารถเรียกวิธีการแบนได้หลายครั้ง ตัวอย่าง (3 มิติ):

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

console.log(arr);

ระวัง!

Array.Flat()วิธีการค่อนข้างใหม่ เบราว์เซอร์รุ่นเก่าเช่นอาจไม่ได้ใช้วิธีการดังกล่าว หากคุณต้องการให้โค้ดทำงานกับเบราว์เซอร์ทั้งหมดคุณอาจต้องเปลี่ยน JS เป็นเวอร์ชันที่เก่ากว่า ตรวจสอบ MD doc ของเว็บเพื่อดูความเข้ากันได้ของเบราว์เซอร์ปัจจุบัน


2
คุณสามารถเรียกเมธอด flat พร้อมInfinityอาร์กิวเมนต์ได้ เช่นนี้:arr.flat(Infinity)
มิคาอิล

นั่นเป็นสิ่งที่ดีมาก ๆ ขอบคุณ Mikhail!
วิลเล็มแวนเดอร์วีน


5

นั่นไม่ใช่เรื่องยากแค่วนซ้ำอาร์เรย์และรวมมันเข้าด้วยกัน:

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

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

5

ดูเหมือนว่านี่จะเป็นงานสำหรับการเดินทาง!

  • จัดการการซ้อนหลายระดับ
  • จัดการอาร์เรย์ว่างเปล่าและพารามิเตอร์ที่ไม่ใช่อาร์เรย์
  • ไม่มีการกลายพันธุ์
  • ไม่พึ่งพาคุณสมบัติเบราว์เซอร์ที่ทันสมัย

รหัส:

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

การใช้งาน:

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

ระวังflatten(new Array(15000).fill([1]))โยนUncaught RangeError: Maximum call stack size exceededและแช่ devTools ของฉันเป็นเวลา 10 วินาที
pietrovismara

@ pietrovismara การทดสอบของฉันประมาณ 1 วินาที
Ivan Yan

5

ฉันทำได้โดยใช้การเรียกซ้ำและปิด

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
เรียบง่ายและอ่อนหวานคำตอบนี้ใช้ได้ดีกว่าคำตอบที่ยอมรับ มันปรับระดับที่ซ้อนกันอย่างลึกล้ำไม่เพียงแค่ระดับแรก
Om Shankar

1
AFAIK ที่กำหนดขอบเขตศัพท์และไม่ใช่การปิด
dashambles

@dashambles ถูกต้อง - ความแตกต่างคือถ้ามันเป็นการปิดคุณจะส่งกลับฟังก์ชันภายในไปสู่ภายนอกและเมื่อฟังก์ชั่นด้านนอกเสร็จสิ้นคุณยังสามารถใช้ฟังก์ชั่นภายในเพื่อเข้าถึงขอบเขตของมัน ที่นี่อายุการใช้งานของฟังก์ชั่นด้านนอกยาวกว่าฟังก์ชั่นด้านในเพื่อไม่ให้มีการ "ปิด"
Mörre

5

ฉันทำผิดกับES6 Generatorsเมื่อวันก่อนและเขียนส่วนสำคัญนี้ ซึ่งประกอบด้วย...

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

โดยทั่วไปฉันกำลังสร้างตัวกำเนิดที่วนซ้ำอาร์เรย์อินพุตดั้งเดิมถ้าพบอาร์เรย์มันจะใช้ตัวดำเนินการyield *ร่วมกับการเรียกซ้ำเพื่อให้แบนภายในอาร์เรย์อย่างต่อเนื่อง หากรายการนั้นไม่ได้อยู่ในอาร์เรย์เพียงแค่ให้ผลเป็นรายการเดียว จากนั้นใช้โอเปอเรเตอร์ ES6 Spread ( splat) ฉันแผ่ตัวสร้างออกเป็นอินสแตนซ์ของอาร์เรย์ใหม่

ฉันไม่ได้ทดสอบประสิทธิภาพของสิ่งนี้ แต่ฉันคิดว่ามันเป็นตัวอย่างง่ายๆของการใช้เครื่องกำเนิดไฟฟ้าและตัวดำเนินการที่ให้ผลตอบแทน *

แต่อีกครั้งฉันแค่ทำผิดดังนั้นฉันแน่ใจว่ามีวิธีที่เหมาะสมในการทำสิ่งนี้มากกว่า


1
คำตอบที่คล้ายกัน (ใช้ตัวสร้างและการมอบหมายผลตอบแทน) แต่ในรูปแบบ PHP
Red Pea

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.