javascript - Chia một mảng JS thành N mảng
Hãy tưởng tượng tôi có một mảng JS như thế này:
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
Điều tôi muốn là chia mảng đó thành N mảng nhỏ hơn. Ví dụ:
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]]
Đối với Python, tôi có cái này:
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
Đối với JS, giải pháp đúng và tốt nhất mà tôi có thể nghĩ ra là một hàm đệ quy, nhưng tôi không thích nó vì nó phức tạp và xấu xí. Hàm bên trong này trả về một mảng như thế này [1, 2, 3, null, 4, 5, 6, null, 7, 8], và sau đó tôi phải lặp lại nó và tách thủ công. (Lần thử đầu tiên của tôi là trả về như sau: [1, 2, 3, [4, 5, 6, [7, 8, 9]]], và tôi đã quyết định làm nó với bộ phân tách null).
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));
}
Đây là một jsfiddle của cái đó: http://jsfiddle.net/uduhH/
Bạn sẽ làm điều đó như thế nào? Cảm ơn!
Câu trả lời #1
Bạn có thể làm cho các lát cắt trở nên "cân bằng" (độ dài của các mảng con khác nhau càng ít càng tốt) hoặc "đều" (tất cả các mảng con ngoại trừ mảng cuối cùng có cùng độ dài):
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>Câu trả lời số 2
Tôi nghĩ cách sử dụng splice theo cách này là gọn gàng nhất:
function splitToNChunks(array, n) {
let result = [];
for (let i = n; i > 0; i--) {
result.push(array.splice(0, Math.ceil(array.length / i)));
}
return result;
}
// Example:
const example = [0,1,2,3,4,5,6,7,8,9,10,11,12]
console.log(splitToNChunks([...example], 3))
console.log(splitToNChunks([...example], 5))Ví dụ, với n=3, bạn sẽ lấy 1/3, sau đó lấy 1/2 phần còn lại, rồi lấy phần còn lại của mảng. Math.ceil đảm bảo rằng trong trường hợp số lượng phần tử không đều, chúng sẽ được chia vào các phần đầu tiên.
(Lưu ý: gọi .splice trên một mảng sẽ trực tiếp thay đổi độ dài của nó. Để tránh làm hỏng mảng ban đầu của bạn, bạn có thể sử dụng bản sao nông tạm thời của mảng thay vì vậy: const copiedArray=[ ...originalArray ])
Câu trả lời #3
function split(array, n) {
let [...arr] = array;
var res = [];
while (arr.length) {
res.push(arr.splice(0, n));
}
return res;
}Câu trả lời #4
Tôi vừa thực hiện một phiên bản lặp của thuật toán: http://jsfiddle.net/ht22q/. Nó vượt qua các trường hợp kiểm tra của bạn.
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;
}
Câu trả lời số 5
Bạn có thể giảm nó thành một ma trận. Ví dụ dưới đây chia mảng (arr) thành một ma trận các mảng hai phần tử. Nếu bạn muốn các kích thước khác, chỉ cần thay đổi giá trị 2 ở dòng thứ hai:
target.reduce((memo, value, index) => {
if (index % 2 === 0 && index !== 0) memo.push([])
memo[memo.length - 1].push(value)
return memo
}, [[]])
Hy vọng điều này hữu ích!
EDIT: Bởi vì một số người vẫn đang bình luận nên điều này không trả lời được câu hỏi vì tôi đang sửa kích thước của mỗi đoạn thay vì số lượng đoạn mà tôi muốn. Ở đây có mã giải thích điều tôi đang cố gắng giải thích trong phần nhận xét: Sử dụng 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>') }Câu trả lời số 6
Cập nhật: 21/7/2020
Câu trả lời mà tôi đã đưa vài năm trước chỉ hoạt động nếu originalArray.length <= numCols. Bạn có thể thay vào đó sử dụng một hàm như bên dưới, nhưng điều đó sẽ tạo ra một bố cục không hoàn toàn khớp với câu hỏi hiện tại (sắp xếp theo hàng thay vì theo cột). Ví dụ: [1,2,3,4] -> [[1,4],[2],[3]]. Tôi hiểu rằng điều này vẫn có thể có giá trị, vì vậy tôi sẽ để nó ở đây, nhưng tôi khuyến nghị câu trả lời của Senthe.
function splitArray(flatArray, numCols){
const newArray = []
for (let c = 0; c < numCols; c++) {
newArray.push([])
}
for (let i = 0; i < flatArray.length; i++) {
const mod = i % numCols
newArray[mod].push(flatArray[i])
}
return newArray
}
Câu trả lời gốc từ năm 2017:
Câu hỏi cũ, nhưng vì vanillaJS không phải là yêu cầu và nhiều người đang cố gắng giải quyết vấn đề này bằng lodash/chunk, và để không nhầm lẫn với những gì _.chunk thực sự làm, đây là một giải pháp ngắn gọn + chính xác sử dụng lodash:
(Không giống như câu trả lời được chấp nhận, điều này cũng đảm bảo n cột ngay cả khi 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
}
Vòng lặp for ở cuối là thứ đảm bảo số lượng "cột" mong muốn.
