แยกอาร์เรย์เป็นชิ้น ๆ


516

สมมติว่าฉันมีอาร์เรย์ Javascript ที่ดูดังต่อไปนี้:

["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.

มีวิธีการใดที่เหมาะสมที่จะทำการแยก (แบ่ง) อาเรย์ออกเป็นอาร์เรย์ขนาดเล็กจำนวนมากด้วยสมมติว่า 10 องค์ประกอบที่มากที่สุด



มีความเป็นไปได้ที่ซ้ำกันของการแบ่งอาร์เรย์ JS ออกเป็นอาร์เรย์ N
TJ

24
สำหรับlodashผู้ใช้ของคุณกำลังมองหา_.chunk
Ulysse BN

2
ฉันเพิ่งลองใช้const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []อันไหนดีและสั้น แต่ดูเหมือนว่าจะใช้เวลาประมาณ 256 ×ตราบเท่าที่คำตอบของ @ AymKdn สำหรับ 1,000 องค์ประกอบและ 1,058 ×นานถึง 10,000 องค์ประกอบ!
Toph

หากคุณต้องการขนาดขั้นต่ำของชิ้นสุดท้ายด้วยนี่คือตัวเลือก: stackoverflow.com/questions/57908133/…
Ahmet Cetin

คำตอบ:


640

array.sliceวิธีสามารถแยกชิ้นจากจุดเริ่มต้นกลางหรือปลายแถวเพื่อวัตถุประสงค์ในสิ่งที่คุณต้องการโดยไม่ต้องเปลี่ยนอาร์เรย์เดิม

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}

19
โปรดจำไว้ว่านี้เป็นฟังก์ชั่น util เพื่อยืนยันกับการเป็นchunk 0(infinite loop)
Steven Lu

19
ไม่ชิ้นอันสุดท้ายควรเล็กกว่าอันอื่น
Blazemonger

7
@ Blazemonger แน่นอน! ครั้งต่อไปฉันจะลองทำเองก่อนที่จะสรุป ฉันสันนิษฐาน (ไม่ถูกต้อง) ว่าการส่งอินพุตไปยัง array.slice ที่เกินขอบเขตของอาร์เรย์จะเป็นปัญหา แต่ใช้งานได้สมบูรณ์แบบ!
rysqui

55
สำหรับหนึ่งสมุทร (โซ่รัก)const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size)); :
КонстантинВан

8
ทำไมคุณต้องเจ ครั้งแรกที่ฉันคิดว่ามันเป็นการเพิ่มประสิทธิภาพ แต่จริง ๆ แล้วมันช้ากว่าสำหรับ (i = 0; i <array.length; i ++) {}
Alex

149

แก้ไขจากคำตอบโดย dbaseman: https://stackoverflow.com/a/10456344/711085

Object.defineProperty(Array.prototype, 'chunk_inefficient', {
  value: function(chunkSize) {
    var array = this;
    return [].concat.apply([],
      array.map(function(elem, i) {
        return i % chunkSize ? [] : [array.slice(i, i + chunkSize)];
      })
    );
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk_inefficient(3)
)
// [[1, 2, 3], [4, 5, 6], [7]]


ภาคผนวกเล็กน้อย :

ฉันควรจะชี้ว่าข้างต้นคือไม่ว่าสง่างาม (ในใจของฉัน) Array.mapวิธีแก้ปัญหากับการใช้งาน โดยทั่วไปแล้วจะทำสิ่งต่อไปนี้โดยที่ ~ ถูกต่อกัน

[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]

มันมีเวลาทำงานแบบ asymptotic เช่นเดียวกับวิธีการด้านล่าง แต่อาจเป็นปัจจัยที่แย่ลงเนื่องจากการสร้างรายการที่ว่างเปล่า หนึ่งสามารถเขียนสิ่งนี้ดังนี้ (ส่วนใหญ่เหมือนกับวิธี Blazemonger ของซึ่งเป็นเหตุผลที่ฉันไม่ได้ส่งคำตอบเดิม):

วิธีที่มีประสิทธิภาพมากขึ้น:

// refresh page if experimenting and you already defined Array.prototype.chunk

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(chunkSize) {
    var R = [];
    for (var i = 0; i < this.length; i += chunkSize)
      R.push(this.slice(i, i + chunkSize));
    return R;
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk(3)
)


วิธีที่ฉันชอบในปัจจุบันคือข้างต้นหรืออย่างใดอย่างหนึ่งต่อไปนี้:

Array.range = function(n) {
  // Array.range(5) --> [0,1,2,3,4]
  return Array.apply(null,Array(n)).map((x,i) => i)
};

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(n) {

    // ACTUAL CODE FOR CHUNKING ARRAY:
    return Array.range(Math.ceil(this.length/n)).map((x,i) => this.slice(i*n,i*n+n));

  }
});

การสาธิต:

> JSON.stringify( Array.range(10).chunk(3) );
[[1,2,3],[4,5,6],[7,8,9],[10]]

หรือถ้าคุณไม่ต้องการฟังก์ชั่น Array.range จริงๆแล้วมันเป็นเพียงซับเดียว (ไม่รวมปุย):

var ceil = Math.ceil;

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array(ceil(this.length/n)).fill().map((_,i) => this.slice(i*n,i*n+n));
}});

หรือ

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
}});

41
เอ๊ะฉันจะหลีกเลี่ยงการไปยุ่งกับต้นแบบเพราะความรู้สึกเย็นสบายที่คุณได้รับจากการเรียกchunkฟังก์ชั่นในอาเรย์นั้นไม่ได้มีความซับซ้อนเกินกว่าความซับซ้อนที่คุณเพิ่มเข้าไปและข้อผิดพลาดเล็ก ๆ
Gordon Gustafson

12
เขาไม่ได้ยุ่งกับพวกเขาเขากำลังขยายพวกเขาสำหรับอาร์เรย์ ฉันเข้าใจว่าไม่เคยสัมผัส Object.prototype เพราะมันจะทำให้วัตถุทั้งหมด (ทุกอย่าง) แต่สำหรับฟังก์ชันเฉพาะของ Array นี้ฉันไม่เห็นปัญหาใด ๆ
rlemon

พริตตี้แน่ใจว่าควรจะเป็นarray.map(function(i)ไม่ได้array.map(function(elem,i)แม้ว่า
ไนเจลแองเจิล

3
อ้างอิงจากแผนภูมิความเข้ากันได้ของไซต์ mozilla dev, Array.map สำหรับสำหรับ IE9 + ระวัง.
Maikel D

ระวังอย่าให้ลอยตัว - ตัวเลข 7.5 ประเภท - นำไปสู่ชิ้นที่คาดเดาไม่ได้
Gal Bracha

103

นี่คือรุ่น ES6 โดยใช้การลด

var perChunk = 2 // items per chunk    

var inputArray = ['a','b','c','d','e']

var result = inputArray.reduce((resultArray, item, index) => { 
  const chunkIndex = Math.floor(index/perChunk)

  if(!resultArray[chunkIndex]) {
    resultArray[chunkIndex] = [] // start a new chunk
  }

  resultArray[chunkIndex].push(item)

  return resultArray
}, [])

console.log(result); // result: [['a','b'], ['c','d'], ['e']]

และคุณพร้อมที่จะเชื่อมโยงแผนที่เพิ่มเติม / ลดการเปลี่ยนแปลง อาร์เรย์อินพุตของคุณถูกต้องไม่เปลี่ยนแปลง


หากคุณต้องการเวอร์ชั่นที่สั้นกว่า แต่อ่านได้น้อยกว่าคุณสามารถโรยบางส่วนconcatลงในส่วนผสมเพื่อผลลัพธ์ที่เหมือนกัน:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])

ดูเหมือนว่าวิธีนี้จะเป็นวิธีที่ย่อที่สุด chunkIndex = Math.floor (index / perChunk) คืออะไร มันเป็นค่าเฉลี่ยหรือไม่
me-me

5/2 = 2.5และMath.floor(2.5) = 2เพื่อให้รายการที่มีค่าดัชนี 5 จะวางไว้ในถัง 2
อังเดรอาร์

ขอบคุณ Andrei R. Ah ดังนั้นมันจึงผ่าน 0 - 2 ดังนั้นสิ่งนี้เรียกว่าในแง่คณิตศาสตร์? ฉันเดาว่าประเด็นของฉันคือฉันไม่เคยคิดที่จะแบ่งดัชนี / 2 ทุกดัชนีเพื่อให้ได้ดัชนีของแต่ละส่วน ดังนั้นฉันจึงพยายามคลุมหัวมันเพราะฉันชอบมันมาก แต่ไม่เข้าใจอย่างเต็มที่ในแง่คณิตศาสตร์ ฉันมักจะทำเช่นนี้เพื่อรับค่าเฉลี่ยของจำนวนทั้งหมด แต่มันก็ดูแตกต่างกัน
me-me

โซลูชันนี้ไม่มีประสิทธิภาพเมื่อเปรียบเทียบกับโซลูชันอื่นเนื่องจากคุณจำเป็นต้องวนซ้ำทุกองค์ประกอบ
JP de la Torre

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

102

พยายามหลีกเลี่ยงการลอกเลียนแบบต้นแบบดั้งเดิมรวมถึง Array.prototype หากคุณไม่รู้ว่าใครจะใช้รหัสของคุณ (บุคคลที่สามผู้ร่วมงานด้วยตัวคุณเองในภายหลังเป็นต้น)

มีวิธีที่จะขยายต้นแบบอย่างปลอดภัย (แต่ไม่ใช่ในเบราว์เซอร์ทั้งหมด) และมีวิธีการใช้วัตถุที่สร้างขึ้นจากต้นแบบที่ขยายอย่างปลอดภัย แต่กฎที่ดีกว่าคือการปฏิบัติตามหลักการของความประหลาดใจอย่างน้อยและหลีกเลี่ยงการปฏิบัติเหล่านี้โดยสิ้นเชิง

หากคุณมีเวลาสักหน่อยให้ดูการพูดคุย JSConf 2011 ของ Andrew Dupont "ทุกอย่างได้รับอนุญาต: การขยายบิวด์อิน"เพื่อการอภิปรายที่ดีเกี่ยวกับหัวข้อนี้

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

function chunk (arr, len) {

  var chunks = [],
      i = 0,
      n = arr.length;

  while (i < n) {
    chunks.push(arr.slice(i, i += len));
  }

  return chunks;
}

// Optionally, you can do the following to avoid cluttering the global namespace:
Array.chunk = chunk;

25
"หลีกเลี่ยงการล้อเล่นกับต้นแบบพื้นเมือง" นักพัฒนา js ใหม่ควรได้รับชั่วคราวถ้าไม่ถาวรสักของวลีนี้
Jacob Dalton

2
ฉันใช้จาวาสคริปต์มาหลายปีแล้วและใช้เวลาไม่นานกับต้นแบบที่ฟังก์ชั่นการโทรที่มากที่สุดไม่เคยปรับเปลี่ยนเหมือนที่คุณเห็นบางคนทำ
1984

2
ข้อเสนอแนะที่ดีที่สุดในสายตาของฉันเข้าใจง่ายที่สุดและในการนำไปใช้ขอขอบคุณมาก!
Olga Farber

1
@JacobDalton ฉันคิดว่ามันเป็นความผิดทั้งหมดของมหาวิทยาลัย ผู้คนคิดว่าต้องใช้ OOP ทุกที่ ดังนั้นพวกเขาจึงกลัวว่า "เพียงแค่สร้างฟังก์ชั่น" พวกเขาต้องการให้แน่ใจว่าได้วางไว้ในบางสิ่ง แม้ว่ามันจะไม่เหมาะสมเลย หากไม่มีเครื่องหมายจุดจะไม่มี "สถาปัตยกรรม"
Gherman

51

ฉันทดสอบคำตอบต่าง ๆ ใน jsperf.com ผลลัพธ์มีอยู่ที่นั่น: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

และฟังก์ชั่นที่เร็วที่สุด (และใช้งานได้กับ IE8) คืออันนี้:

function chunk(arr, chunkSize) {
  var R = [];
  for (var i=0,len=arr.length; i<len; i+=chunkSize)
    R.push(arr.slice(i,i+chunkSize));
  return R;
}

1
ขอบคุณ @AymKdn สำหรับการทำเกณฑ์นี้: มีประโยชน์มาก! ฉันใช้วิธีการประกบกันและทำให้เบราว์เซอร์ Chrome v70 ของฉันที่ขนาด 884432 ขัดข้องเมื่อใช้วิธี "slice" ที่แนะนำของคุณรหัสของฉันไม่ผิดพลาดในกระบวนการ "เรนเดอร์" ของเบราว์เซอร์อีกต่อไป :)
Benny Neugebauer

34

ฉันต้องการใช้วิธีการต่อรอย :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};

16
จะต้องระมัดระวังด้วยวิธีการต่อรอยนี้เพราะมันแก้ไขอาเรย์เดิมดังนั้นให้แน่ใจว่าเอกสารผลข้างเคียงอย่างชัดเจน
bdrx

3
จากนั้นใช้ชิ้นส่วนแทน
mplungjan

@mplungjan ผลลัพธ์จะเป็นอาร์เรย์เดียวกันซ้ำแล้วซ้ำอีกเมื่อใช้ slice ดังนั้นจึงไม่ใช่การแทนที่แบบดรอปดาวน์โดยไม่มีการแก้ไขเพิ่มเติม
nietonfir

สิ่งเดียวที่ฉันจะเพิ่มคำตอบนี้คือโคลนไปยังอาร์เรย์เดิม ฉันจะทำเช่นนั้นกับผู้ดำเนินการแพร่กระจายของ ES6 var clone = [...array]จากนั้นทำการตรวจสอบความยาวและทำการต่อแถวของโคลนนั้น
MauricioLeal

1
หรือถ้าคุณไม่สามารถใช้คุณสมบัติ ES6 คุณก็สามารถarray = array.slice()สร้างสำเนาตื้น ๆ ได้เช่นกัน
3limin4t0r

34

สายการบินเดียวใน ECMA 6

const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]

new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))

9
มันแก้ไขlistอาเรย์ดั้งเดิม
Jacka

6
แก้ไขได้อย่างง่ายดายโดยใช้ .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey

2
ใน JSPerf สิ่งนี้มีประสิทธิภาพมากกว่าคำตอบอื่น ๆ
คาเฮนหนิง

30

คำถามเก่า: คำตอบใหม่! ที่จริงฉันทำงานกับคำตอบจากคำถามนี้และมีเพื่อนปรับปรุงมัน! ดังนั้นนี่คือ:

Array.prototype.chunk = function ( n ) {
    if ( !this.length ) {
        return [];
    }
    return [ this.slice( 0, n ) ].concat( this.slice(n).chunk(n) );
};

[1,2,3,4,5,6,7,8,9,0].chunk(3);
> [[1,2,3],[4,5,6],[7,8,9],[0]]

14
fyi ประสิทธิภาพของวิธีนี้คือ O (N ^ 2) ดังนั้นจึงไม่ควรใช้ในส่วนที่สำคัญต่อประสิทธิภาพของรหัสหรือด้วยอาร์เรย์ที่ยาว (โดยเฉพาะเมื่ออาร์เรย์.lengthมีขนาดใหญ่กว่าขนาดของแถวn) หากนี่เป็นภาษาที่ขี้เกียจ (ต่างจากจาวาสคริปต์) อัลกอริทึมนี้จะไม่ประสบกับเวลา O (N ^ 2) ที่กล่าวว่าการใช้งานแบบเรียกซ้ำนั้นสวยงาม คุณสามารถแก้ไขเพื่อปรับปรุงประสิทธิภาพโดยการกำหนดฟังก์ชั่นตัวช่วยที่เกิดขึ้นซ้ำarray,positionแล้วส่งไป: Array.prototype.chunkส่งคืน [ฟังก์ชันผู้ช่วยของคุณ] (... )
ninjagecko

ขอบคุณ sensai สำหรับการให้พรฉันด้วยจิตวิญญาณของคุณที่ฉันจะต้องมี chanelled คืนนี้
กัด

1
หรือ ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams

28

ทุกวันนี้คุณสามารถใช้ฟังก์ชั่นอันสั้นของ lodash เพื่อแบ่งอาร์เรย์ออกเป็นอาร์เรย์ขนาดเล็กลงhttps://lodash.com/docs#chunkไม่จำเป็นต้องเล่นกับวงอีกต่อไป!


3
ฉันรู้สึกว่าควรมีข้อจำกัดความรับผิดชอบต่อคำถามจาวาสคริปต์: คุณลองพักแล้วหรือยัง? ค่อนข้างมากสิ่งแรกที่ฉันรวมไว้ในโหนดหรือเบราว์เซอร์
Jacob Dalton

20

มีคำตอบมากมาย แต่นี่คือสิ่งที่ฉันใช้:

const chunk = (arr, size) =>
  arr
    .reduce((acc, _, i) =>
      (i % size)
        ? acc
        : [...acc, arr.slice(i, i + size)]
    , [])

// USAGE
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chunk(numbers, 3)

// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

ขั้นแรกให้ตรวจสอบส่วนที่เหลือเมื่อทำการหารดัชนีด้วยขนาดก้อน

หากมีเศษเหลืออยู่ให้ส่งคืนอาร์เรย์ตัวสะสม

หากไม่มีเศษเหลืออยู่ดัชนีจะถูกหารด้วยขนาดก้อนดังนั้นนำชิ้นส่วนจากอาร์เรย์เดิม (เริ่มต้นที่ดัชนีปัจจุบัน) และเพิ่มลงในอาร์เรย์ตัวสะสม

ดังนั้นอาร์เรย์แอคคิวมูเลเตอร์ที่ส่งคืนสำหรับการลดซ้ำแต่ละครั้งจะมีลักษณะดังนี้:

// 0: [[1, 2, 3]]
// 1: [[1, 2, 3]]
// 2: [[1, 2, 3]]
// 3: [[1, 2, 3], [4, 5, 6]]
// 4: [[1, 2, 3], [4, 5, 6]]
// 5: [[1, 2, 3], [4, 5, 6]]
// 6: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 7: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 8: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 9: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

การแก้ปัญหาที่ดีและการแสดงภาพที่ดีของการทำซ้ำ ฉันสิ้นสุดด้วยโซลูชันที่คล้ายกันมากซึ่งฉันโพสต์เป็นคำตอบ: stackoverflow.com/a/60779547
เอ็มทีเอ knn

15

การใช้เครื่องกำเนิดไฟฟ้า

function* chunks(arr, n) {
 for(let i = 0; i < arr.length; i += n) {
     yield(arr.slice(i, i+n));
     }
}
let someArray = [0,1,2,3,4,5,6,7,8,9]
console.log([...chunks(someArray, 2)]) // [[0,1],[2,3],[4,5],[6,7],[8,9]]


3
ฉันรู้สึกประหลาดใจกับคำตอบทั้งหมดของคำถามนี้ที่เกือบจะใช้เครื่องกำเนิดไฟฟ้าหรือใช้พวกมันในวิธีที่ซับซ้อนกว่า ความกระชับและประสิทธิภาพที่ดีด้วยโซลูชันนี้
Keith

14

โอเคเรามาเริ่มกันดีกว่า:

function chunk(arr, n) {
    return arr.slice(0,(arr.length+n-1)/n|0).
           map(function(c,i) { return arr.slice(n*i,n*i+n); });
}

ซึ่งใช้แบบนี้:

chunk([1,2,3,4,5,6,7], 2);

จากนั้นเรามีฟังก์ชั่นลดแรงตึงตัวนี้:

function chunker(p, c, i) {
    (p[i/this|0] = p[i/this|0] || []).push(c);
    return p;
}

ซึ่งใช้แบบนี้:

[1,2,3,4,5,6,7].reduce(chunker.bind(3),[]);

เนื่องจากลูกแมวตายเมื่อเราผูกติดthisกับตัวเลขเราสามารถทำการแกงด้วยมือแบบนี้แทน:

// Fluent alternative API without prototype hacks.
function chunker(n) {
   return function(p, c, i) {
       (p[i/n|0] = p[i/n|0] || []).push(c);
       return p;
   };
}

ซึ่งใช้แบบนี้:

[1,2,3,4,5,6,7].reduce(chunker(3),[]);

จากนั้นฟังก์ชั่นยังค่อนข้างแน่นซึ่งทำได้ในครั้งเดียว:

function chunk(arr, n) {
    return arr.reduce(function(p, cur, i) {
        (p[i/n|0] = p[i/n|0] || []).push(cur);
        return p;
    },[]);
}

chunk([1,2,3,4,5,6,7], 3);

ไม่ทำงานใน iE8
Nadeemmnn Mohd

4
ฮา! ฉันชอบความคิดเห็นของลูกแมว ขออภัยในความไม่สร้างสรรค์การป้อนข้อมูลเพิ่มเติม :)
29er

ฉันจะทำ(p[i/n|0] || (p[i/n|0] = []))เพื่อให้คุณไม่ได้กำหนดค่าถ้าไม่จำเป็น ...
yckart

12

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

function chunk(a, l) { 
    return new Array(Math.ceil(a.length / l)).fill(0)
        .map((_, n) => a.slice(n*l, n*l + l)); 
}

รุ่นที่มีการเรียกซ้ำครั้งนี้ดูง่ายขึ้นและน่าสนใจยิ่งขึ้น:

function chunk(a, l) { 
    if (a.length == 0) return []; 
    else return [a.slice(0, l)].concat(chunk(a.slice(l), l)); 
}

ฟังก์ชันอาเรย์ที่อ่อนแออย่างน่าขันของ ES6 สร้างขึ้นเพื่อไขปริศนาที่ดี :-)


1
ฉันยังเขียนของฉันมากเช่นนี้ มันยังใช้งานได้ถ้าคุณลบมันออก0จากfillซึ่งทำให้fillดูมีเหตุผลมากขึ้น imho
Coert Grobbelaar

12

ฉันคิดว่านี่เป็นโซลูชันแบบเรียกซ้ำที่มีไวยากรณ์ ES6:

const chunk = function(array, size) {
  if (!array.length) {
    return [];
  }
  const head = array.slice(0, size);
  const tail = array.slice(size);

  return [head, ...chunk(tail, size)];
};

console.log(chunk([1,2,3], 2));


9

สร้างแพคเกจ npm สำหรับhttps://www.npmjs.com/package/array.chunkนี้

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.slice(i, size + i));
}
return result;

เมื่อใช้TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;

@ A1rPun ฉันไม่ดีฉันไม่ได้เพิ่มความคิดเห็นที่นั่น ใช่ไม่มีsliceวิธีสำหรับTypedArrayเราสามารถใช้subarrayแทนdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee

8

หากคุณใช้ EcmaScript เวอร์ชัน> = 5.1 คุณสามารถใช้เวอร์ชันการทำงานของการchunk()ใช้array.reduce ()ที่มีความซับซ้อน O (N):

function chunk(chunkSize, array) {
    return array.reduce(function(previous, current) {
        var chunk;
        if (previous.length === 0 || 
                previous[previous.length -1].length === chunkSize) {
            chunk = [];   // 1
            previous.push(chunk);   // 2
        }
        else {
            chunk = previous[previous.length -1];   // 3
        }
        chunk.push(current);   // 4
        return previous;   // 5
    }, []);   // 6
}

console.log(chunk(2, ['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e' ] ]

คำอธิบายของแต่ละ// nbrข้างต้น:

  1. สร้างอันใหม่ถ้าค่าก่อนหน้าเช่นอาร์เรย์ที่ส่งคืนก่อนหน้าของชิ้นว่างเปล่าหรือถ้าอันก่อนหน้านี้มี chunkSizeรายการ
  2. เพิ่มอันใหม่ลงในอาร์เรย์ของชิ้นที่มีอยู่
  3. มิฉะนั้นก้อนปัจจุบันคืออันสุดท้ายในอาร์เรย์ของชิ้น
  4. เพิ่มมูลค่าปัจจุบันให้กับก้อน
  5. ส่งคืนอาร์เรย์ของกลุ่มที่ถูกดัดแปลง
  6. เริ่มต้นการลดลงโดยผ่านอาร์เรย์ที่ว่างเปล่า

แกงตามchunkSize:

var chunk3 = function(array) {
    return chunk(3, array);
};

console.log(chunk3(['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]

คุณสามารถเพิ่มchunk()ฟังก์ชั่นให้กับArrayวัตถุทั่วโลก:

Object.defineProperty(Array.prototype, 'chunk', {
    value: function(chunkSize) {
        return this.reduce(function(previous, current) {
            var chunk;
            if (previous.length === 0 || 
                    previous[previous.length -1].length === chunkSize) {
                chunk = [];
                previous.push(chunk);
            }
            else {
                chunk = previous[previous.length -1];
            }
            chunk.push(current);
            return previous;
        }, []);
    }
});

console.log(['a', 'b', 'c', 'd', 'e'].chunk(4));
// prints [ [ 'a', 'b', 'c' 'd' ], [ 'e' ] ]


7
in coffeescript:

b = (a.splice(0, len) while a.length)

demo 
a = [1, 2, 3, 4, 5, 6, 7]

b = (a.splice(0, 2) while a.length)
[ [ 1, 2 ],
  [ 3, 4 ],
  [ 5, 6 ],
  [ 7 ] ]

a.splice (0, 2) ลบ subarray ของ [0..1] ออกจาก a และส่งคืน subarray a [0..1] ฉันกำลังทำอาร์เรย์ของอาร์เรย์เหล่านั้นทั้งหมด
Arpit Jain

2
ฉันขอแนะนำให้ใช้วิธีการแบบไม่ทำลาย () แทนการประกบกัน ()
Ash Blue

7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}

1
ไม่แน่ใจว่าทำไมสิ่งนี้ถึงถูกโหวต แต่รหัสอาจใช้คำอธิบายบางอย่าง
jpaugh

2
เพราะรอยต่อเป็นอันตรายต่ออาร์เรย์ดั้งเดิม
metalim

7

วิธี ES2015 ต่อไปนี้ทำงานได้โดยไม่ต้องกำหนดฟังก์ชั่นและโดยตรงบนอาร์เรย์ที่ไม่ระบุตัวตน (ตัวอย่างที่มีขนาดอัน 2):

[11,22,33,44,55].map((_, i, all) => all.slice(2*i, 2*i+2)).filter(x=>x.length)

หากคุณต้องการกำหนดฟังก์ชั่นสำหรับสิ่งนี้คุณสามารถทำได้ดังนี้ (ปรับปรุงความคิดเห็นของ K ._ ต่อคำตอบของ Blazemonger ):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)

6

และนี่จะเป็นผลงานของฉันในหัวข้อนี้ ฉันเดาว่า.reduce()เป็นวิธีที่ดีที่สุด

var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
                                                    : (r.push([e]), r), []),
        arr = Array.from({length: 31}).map((_,i) => i+1);
        res = segment(arr,7);
console.log(JSON.stringify(res));

แต่การนำไปใช้ข้างต้นนั้นไม่ได้มีประสิทธิภาพมากนักเมื่อทำการ.reduce()ผ่านarrฟังก์ชั่นทั้งหมด วิธีที่มีประสิทธิภาพมากขึ้น (ใกล้กับโซลูชั่นที่จำเป็นที่สุด) คือทำซ้ำในอาร์เรย์ที่ลดลง (จะถูกบีบอัด) เนื่องจากเราสามารถคำนวณขนาดล่วงหน้าMath.ceil(arr/n);ได้ เมื่อเรามีอาร์เรย์ผลว่างเปล่าเช่นArray(Math.ceil(arr.length/n)).fill();ที่เหลือคือการแมปชิ้นของarrอาร์เรย์ลงไป

function chunk(arr,n){
  var r = Array(Math.ceil(arr.length/n)).fill();
  return r.map((e,i) => arr.slice(i*n, i*n+n));
}

arr = Array.from({length: 31},(_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));



5

อีกวิธีใช้arr.reduce():

const chunk = (arr, size) => (
  arr.reduce((acc, _, i) => {
    if (i % size === 0) acc.push(arr.slice(i, i + size))
    return acc
  }, [])
)

// Usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const chunked = chunk(numbers, 3)
console.log(chunked)

โซลูชันนี้คล้ายกับโซลูชันของ Steve Holgadoมาก อย่างไรก็ตามเนื่องจากวิธีนี้ไม่ได้ใช้การแพร่กระจายของอาเรย์และไม่ได้สร้างอาร์เรย์ใหม่ในฟังก์ชั่นลดจึงเร็วกว่า (ดูการทดสอบ jsPerf ) และอ่านได้ง่ายกว่า (ไวยากรณ์ที่ง่ายกว่า) กว่าโซลูชันอื่น

ที่การวนซ้ำทุกครั้งที่n (โดยที่n = size; เริ่มต้นการทำซ้ำครั้งแรก) อาร์เรย์ตัวสะสม ( acc) จะถูกต่อท้ายด้วยชุดของแถว (arr.slice(i, i + size) ) แล้วส่งคืน ในการวนซ้ำอื่น ๆ อาร์เรย์ตัวสะสมจะถูกส่งกลับตามสภาพเดิม

ถ้าsizeเป็นศูนย์วิธีการส่งกลับอาร์เรย์ที่ว่างเปล่า ถ้าsizeเป็นลบเมธอดจะส่งคืนผลลัพธ์ที่เสียหาย ดังนั้นหากจำเป็นในกรณีของคุณคุณอาจต้องการทำอะไรบางอย่างเกี่ยวกับค่าลบหรือไม่ใช่sizeค่าบวก


หากความเร็วมีความสำคัญในกรณีของคุณการforวนรอบแบบง่ายจะเร็วกว่าการใช้arr.reduce()(ดูการทดสอบ jsPerf ) และบางคนอาจพบว่าสไตล์นี้อ่านง่ายขึ้นเช่นกัน:

function chunk(arr, size) {
  // This prevents infinite loops
  if (size < 1) throw new Error('Size must be positive')

  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}

4

สำหรับโซลูชันที่ใช้งานได้โดยใช้Ramda :

popularProductsอาร์เรย์อินพุตของคุณอยู่ที่ไหน5ขนาดก้อน

import splitEvery from 'ramda/src/splitEvery'

splitEvery(5, popularProducts).map((chunk, i) => {
// do something with chunk

})


4

ES6 one-line approach ตามArray.prototype reduceและpushวิธีการ:

const doChunk = (list, size) => list.reduce((r, v) =>
  (!r.length || r[r.length - 1].length === size ?
    r.push([v]) : r[r.length - 1].push(v)) && r
, []);

console.log(doChunk([0,1,2,3,4,5,6,7,8,9,10,11,12], 5));
// [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12]]


4

นี่เป็นโซลูชันที่มีประสิทธิภาพและตรงไปตรงมาที่สุดที่ฉันสามารถนึกได้:

function chunk(array, chunkSize) {
    let chunkCount = Math.ceil(array.length / chunkSize);
    let chunks = new Array(chunkCount);
    for(let i = 0, j = 0, k = chunkSize; i < chunkCount; ++i) {
        chunks[i] = array.slice(j, k);
        j = k;
        k += chunkSize;
    }
    return chunks;
}


4

ต่อไปนี้เป็นโซลูชันแบบเรียกซ้ำที่เหมาะสำหรับการโทรหาง

const splitEvery = (n, xs, y=[]) =>
  xs.length===0 ? y : splitEvery(n, xs.slice(n), y.concat([xs.slice(0, n)])) 

console.log(splitEvery(2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))


2

แก้ไข: @ mblase75 เพิ่มรหัสที่กระชับมากขึ้นในคำตอบก่อนหน้านี้ในขณะที่ฉันกำลังเขียนของฉันดังนั้นฉันขอแนะนำให้ไปกับการแก้ปัญหาของเขา

คุณสามารถใช้รหัสเช่นนี้:

var longArray = ["Element 1","Element 2","Element 3", /*...*/];
var smallerArrays = []; // will contain the sub-arrays of 10 elements each
var arraySize = 10;
for (var i=0;i<Math.ceil(longArray.length/arraySize);i++) {
    smallerArrays.push(longArray.slice(i*arraySize,i*arraySize+arraySize));
}

เปลี่ยนค่าเป็นarraySizeเพื่อเปลี่ยนความยาวสูงสุดของอาร์เรย์ที่เล็กลง


2

นี่คือโซลูชันที่ไม่กลายพันธุ์โดยใช้การเรียกซ้ำและการแบ่งเท่านั้น ()

const splitToChunks = (arr, chunkSize, acc = []) => (
    arr.length > chunkSize ?
        splitToChunks(
            arr.slice(chunkSize),
            chunkSize,
            [...acc, arr.slice(0, chunkSize)]
        ) :
        [...acc, arr]
);

จากนั้นก็ใช้มันเหมือนsplitToChunks([1, 2, 3, 4, 5], 3)ได้รับ[[1, 2, 3], [4, 5]]ที่จะได้รับ

นี่คือซอให้คุณทดลองใช้: https://jsfiddle.net/6wtrbx6k/2/

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