2011-11-18 92 views
47

想象我有一個JS陣列是這樣的:分裂一個JS陣列分成N個陣列

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

我想是該數組分成N個較小的陣列。例如:

split_list_in_n(a, 2) 
[[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11]] 

For N = 3: 
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]] 

For N = 4: 
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]] 

For N = 5: 
[[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11]] 

對於Python,我有這樣的:

def split_list_in_n(l, cols): 
    """ Split up a list in n lists evenly size chuncks """ 
    start = 0 
    for i in xrange(cols): 
     stop = start + len(l[i::cols]) 
     yield l[start:stop] 
     start = stop 

對於JS,我能拿出最好的正確的解決方案是一個遞歸函數,但我不喜歡它,因爲它很複雜和難看。這個內部函數返回一個像這樣的數組[1,2,3],null,4,5,6,null,7,8],然後我必須再次循環並手動分割它。 (我的第一次嘗試是返回這個:[1,2,3,[4,5,6,[7,8,9]],並且我決定用空分隔符來做)。

function split(array, cols) { 
    if (cols==1) return array; 
    var size = Math.ceil(array.length/cols); 
    return array.slice(0, size).concat([null]).concat(split(array.slice(size), cols-1)); 
} 

這裏有一個的jsfiddle:http://jsfiddle.net/uduhH/

你會怎麼做呢?謝謝!

+1

有關 - http://stackoverflow.com/q/40166199/104380 – vsync

回答

102

您可以切片「平衡」(子陣的長度不同,儘可能少)或‘偶’(所有子陣列,但最後有相同的長度):

function chunkify(a, n, balanced) { 
 
    
 
    if (n < 2) 
 
     return [a]; 
 

 
    var len = a.length, 
 
      out = [], 
 
      i = 0, 
 
      size; 
 

 
    if (len % n === 0) { 
 
     size = Math.floor(len/n); 
 
     while (i < len) { 
 
      out.push(a.slice(i, i += size)); 
 
     } 
 
    } 
 

 
    else if (balanced) { 
 
     while (i < len) { 
 
      size = Math.ceil((len - i)/n--); 
 
      out.push(a.slice(i, i += size)); 
 
     } 
 
    } 
 

 
    else { 
 

 
     n--; 
 
     size = Math.floor(len/n); 
 
     if (len % size === 0) 
 
      size--; 
 
     while (i < size * n) { 
 
      out.push(a.slice(i, i += size)); 
 
     } 
 
     out.push(a.slice(size * n)); 
 

 
    } 
 

 
    return out; 
 
} 
 

 

 
/////////////////////// 
 

 
onload = function() { 
 
    function $(x) { 
 
     return document.getElementById(x); 
 
    } 
 

 
    function calc() { 
 
     var s = +$('s').value, a = []; 
 
     while (s--) 
 
      a.unshift(s); 
 
     var n = +$('n').value; 
 
     $('b').textContent = JSON.stringify(chunkify(a, n, true)) 
 
     $('e').textContent = JSON.stringify(chunkify(a, n, false)) 
 
    } 
 

 
    $('s').addEventListener('input', calc); 
 
    $('n').addEventListener('input', calc); 
 
    calc(); 
 
}
<p>slice <input type="number" value="20" id="s"> items into 
 
<input type="number" value="6" id="n"> chunks:</p> 
 
<pre id="b"></pre> 
 
<pre id="e"></pre>

+0

你的解決方案很整潔,它和我的遞歸解決方案一樣,但沒有任何混亂。謝謝! – Tiago

+2

工程就像一個魅力..很好的解決方案 – Vardan

+0

嗨@georg,請你解釋一下這一行: 'var size = Math。ceil((len-i)/ n - );' – dpg5000

5

我剛剛做了一個迭代實現的算法:http://jsfiddle.net/ht22q/。它通過你的測試用例。

function splitUp(arr, n) { 
    var rest = arr.length % n, // how much to divide 
     restUsed = rest, // to keep track of the division over the elements 
     partLength = Math.floor(arr.length/n), 
     result = []; 

    for(var i = 0; i < arr.length; i += partLength) { 
     var end = partLength + i, 
      add = false; 

     if(rest !== 0 && restUsed) { // should add one element for the division 
      end++; 
      restUsed--; // we've used one division element now 
      add = true; 
     } 

     result.push(arr.slice(i, end)); // part of the array 

     if(add) { 
      i++; // also increment i in the case we added an extra element for division 
     } 
    } 

    return result; 
} 
+1

(可正常工作,但我只能選擇一個答案是正確的)嗨!感謝您的幫助。尼斯考慮如何使用其餘部分。 – Tiago

2

遞歸方法,未經測試。

function splitArray(array, parts, out) { 
    var 
     len = array.length 
     , partLen 

    if (parts < len) { 
     partLen = Math.ceil(len/parts); 
     out.push(array.slice(0, partLen)); 
     if (parts > 1) { 
      splitArray(array.slice(partLen), parts - 1, out); 
     } 
    } else { 
     out.push(array); 
    } 
} 
0

我做到了這種方式,它的工作原理...

function splitArray(array, parts) { 
    if (parts< array.length && array.length > 1 && array != null) { 
     var newArray = []; 
     var counter1 = 0; 
     var counter2 = 0; 

     while (counter1 < parts) { 
      newArray.push([]); 
      counter1 += 1; 
     } 

     for (var i = 0; i < array.length; i++) { 
      newArray[counter2++].push(array[i]); 
      if (counter2 > parts - 1) 
       counter2 = 0; 
     } 

     return newArray; 
    } else 
     return array; 
} 
2

另一個遞歸的作品非常好,它是不太難看

function nSmaller(num, arr, sliced) { 

    var mySliced = sliced || []; 
    if(num === 0) { 
     return sliced; 
    } 

    var len = arr.length, 
     point = Math.ceil(len/num), 
     nextArr = arr.slice(point); 

    mySliced.push(arr.slice(0, point)); 
    nSmaller(num-1, nextArr, mySliced); 

    return(mySliced); 
} 
6

function split(arr, n) { 
 
    var res = []; 
 
    while (arr.length) { 
 
    res.push(arr.splice(0, n)); 
 
    } 
 
    return res; 
 
}

+1

對於n = 5和arr = [1,2,3,4,5,6,7,8,9,10,11],這不起作用。 – Tiago

+0

這不會分裂成n個子陣列,僅僅是n個子陣列。 – dpg5000

+0

請添加一些解釋爲什麼此代碼可以幫助OP。這將有助於提供未來觀衆可以從中學習的答案。有關更多信息,請參閱[答案]。 –

6

你可以紅色把它變成矩陣。下面的示例將數組(arr)拆分爲兩個位置數組的矩陣。如果你想要其他尺寸只需更改第二行的2值:

target.reduce((memo, value, index) => { 
    if (index % 2 == 0 && index !== 0) memo.push([]) 
    memo[memo.length - 1].push(value) 
    return memo 
}, [[]]) 

希望它有幫助!

編輯:因爲有些人仍然評論這不會回答這個問題,因爲我正在修復每個塊的大小,而不是我想要的塊的數量。這裏是解釋我想在註釋部分解釋的代碼:使用target.length

// Chunk function 
 

 
const chunk = (target, size) => { 
 
    return target.reduce((memo, value, index) => { 
 
    // Here it comes the only difference 
 
    if (index % (target.length/size) == 0 && index !== 0) memo.push([]) 
 
    memo[memo.length - 1].push(value) 
 
    return memo 
 
    }, [[]]) 
 
} 
 

 
// Usage 
 

 
write(chunk([1, 2, 3, 4], 2)) 
 
write(chunk([1, 2, 3, 4], 4)) 
 

 
// For rendering pruposes. Ignore 
 
function write (content) { document.write(JSON.stringify(content), '</br>') }

+2

哇,很好,簡潔的方式來做到這一點!愛它!做得好! :-) –

+0

我喜歡這種技術,但它並沒有回答這個問題。它返回任意數量的x大小的塊,而問題是要求x個均勻大小的塊。 –

+3

喜歡這個!我重構了返回均勻大小的塊'函數splitArr(arr,n){arr.length(arr,n)/n){a.push([])} a [a.length - 1] .push(i) return a; },[[]]) }' –

0

檢查我的版本陣列的拆分

// divide array 
Array.prototype.divideIt = function(d){ 
    if(this.length <= d) return this; 
    var arr = this, 
     hold = [], 
     ref = -1; 
    for(var i = 0; i < arr.length; i++){ 
     if(i % d === 0){ 
      ref++; 
     } 
     if(typeof hold[ref] === 'undefined'){ 
      hold[ref] = []; 
     } 
     hold[ref].push(arr[i]); 
    } 

    return hold; 
}; 
1

,如果你知道想設置child_arrays.length的話,我覺得這個解決方案最:

function sp(size, arr){ //size - child_array.length 
    var out = [],i = 0, n= Math.ceil((arr.length)/size); 
    while(i < n) { out.push(arr.splice(0, (i==n-1) && size < arr.length ? arr.length: size)); i++;} 
    return out; 
} 

通話fn: sp(2,[1,2,3,4,5,6 ,7,8,9,10,11])// 2 - child_arrat.length

回答: [1,2],[3,4],[5,6],[7,8], [9,10],[11]

0

只需使用lodash'chunk函數將數組分割成更小的數組https://lodash.com/docs#chunk不需要再次使用循環!

+0

這個問題問如何解決這個使用vannila js,不使用js庫 –

+2

感謝您提出這個問題。我不知道洛達什有這個。 – Tiago

+8

這也沒有回答這個問題。他想要N個數組,而不是N個元素的數組。 – mAAdhaTTah

1

也許更清潔的方法是以下(不使用任何其他庫):

var myArray = []; 
for(var i=0; i<100; i++){ 
    myArray.push(i+1); 
} 
console.log(myArray); 

function chunk(arr, size){ 
    var chunkedArr = []; 
    var noOfChunks = Math.ceil(arr.length/size); 
    console.log(noOfChunks); 
    for(var i=0; i<noOfChunks; i++){ 
    chunkedArr.push(arr.slice(i*size, (i+1)*size)); 
    } 
    return chunkedArr; 
} 

var chunkedArr = chunk(myArray, 3); 
console.log(chunkedArr); 

我已經創建了自己的陣列將被分塊。你可以找到代碼here

此外,我們在lodash庫中有一個方法「chunk」,它非常有用。希望幫助

-3

如果您正在使用lodash,你可以很輕鬆地實現它象下面這樣:

import {chunk} from 'lodash'; 
// divides the array into 2 sections 
chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2); // => [[1,2,3,4,5,6], [7,8,9,10,11]] 
+3

這是錯誤的。 '_.chunk'創建N個元素而不是N個數組的數組。 你的例子會有6個數組的輸出,每個數組有2個元素,每個元素最後一個'''[[1,2],[3,4],[5,6],[7,8],[9, 10],[11]]''' –

+0

那是什麼原始問題。請閱讀問題中的預期行爲。 – abhisekpaul

3

老問題,但由於vanillaJS不是必需的,因此許多人都試圖用lodash解決這一/塊,並沒有弄錯什麼_.chunk實際上做了,這是一個使用lodash簡潔+準確的解決方案:

(不像公認的答案,這也保證了n列即使originalArray.length < numCols

import _chunk from 'lodash/chunk' 

/** 
* Split an array into n subarrays (or columns) 
* @param {Array} flatArray Doesn't necessarily have to be flat, but this func only works 1 level deep 
* @param {Number} numCols The desired number of columns 
* @return {Array} 
*/ 
export function splitArray(flatArray, numCols){ 
    const maxColLength = Math.ceil(flatArray.length/numCols) 
    const nestedArray = _chunk(flatArray, maxColLength) 
    let newArray = [] 
    for (var i = 0; i < numCols; i++) { 
    newArray[i] = nestedArray[i] || [] 
    } 
    return newArray 
} 

for最後循環是保證所需數量的「列」。

0

如果你可以使用lodash並希望函數式編程的方法,這裏是我想出:

const _ = require('lodash') 

function splitArray(array, numChunks) { 
    return _.reduce(_.range(numChunks), ({array, result, numChunks}, chunkIndex) => { 
    const numItems = Math.ceil(array.length/numChunks) 
    const items = _.take(array, numItems) 
    result.push(items) 
    return { 
     array: _.drop(array, numItems), 
     result, 
     numChunks: numChunks - 1 
    } 
    }, { 
    array, 
    result: [], 
    numChunks 
    }).result 
} 
0
上述所有

可能工作得很好,但如果你有associative陣列字符串作爲鍵?

objectKeys = Object.keys; 

arraySplit(arr, n) { 
    let counter = 0; 
    for (const a of this.objectKeys(arr)) { 
     this.arr[(counter%n)][a] = arr[a]; 
     counter++; 
    } 
} 
0
function splitArray(arr, numOfParts = 10){ 
     const splitedArray = [] 
     for (let i = 0; i < numOfParts;i++) { 
      const numOfItemsToSplice = arr.length/10; 
      splitedArray.push(arr.splice(0, numOfItemsToSplice)) 
     } 
     return splitedArray; 
    }