JS 배열을 N 배열로 분할


82

다음과 같은 JS 배열이 있다고 상상해보십시오.

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]]], 그리고 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));
}

여기에 jsfiddle이 있습니다 : http://jsfiddle.net/uduhH/

어떻게 하시겠습니까? 감사!



1
당신의 split기능은 멀지 않습니다. null두 개의 어레이 래퍼 ( if (cols == 1) return [array]및)를 추가 하여 비즈니스를 제거 할 수 있습니다 return [array.slice(0, size)].concat(split(array.slice(size), cols-1)). 이 재귀 버전은 여기에있는 대부분의 답변보다 훨씬 더 읽기 쉽습니다.
Scott Sauyet

답변:


134

슬라이스를 "균형"(서버 레이의 길이가 가능한 한 작게 다름) 또는 "균등"(모든 서브 어레이지만 마지막은 동일한 길이)으로 만들 수 있습니다.

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>


귀하의 솔루션은 깔끔합니다. 내 재귀 솔루션과 동일한 작업을 수행하지만 모든 것이 엉망입니다. 감사합니다!
Tiago 2011

3
작품 매력 .. 니스 솔루션
Vardan

안녕하세요 @georg,이 줄을 설명해 주시겠습니까? var size = Math.ceil((len - i) / n--);
dpg5000

@ dpg5000 : 다음 청크를 슬라이싱 할 때 크기는 나머지 요소의 수 ( len - i)와 나머지 청크의 수 ( n--)입니다
georg

1
안녕하세요 @georg 감사합니다. 마지막 하위 배열을 제외하고 모든 하위 배열의 길이가 동일한 지 확인하기 위해이 코드를 수정하려면 어떻게해야합니까 (물론 제수가 나머지가 없어서 모든 하위 배열이 동일하지 않는 한). 도움을 주셔서 감사합니다.
dpg5000

19

스플 라이스를 사용하는 것이 가장 깨끗하다고 ​​생각합니다.

splitToChunks(array, parts) {
    let result = [];
    for (let i = parts; i > 0; i--) {
        result.push(array.splice(0, Math.ceil(array.length / i)));
    }
    return result;
}

예를 들어,의 경우 parts = 31/3, 나머지 부분의 1/2, 나머지 배열을 차례로 사용합니다. Math.ceil요소 수가 고르지 않은 경우 가장 빠른 청크로 이동합니다.

(참고 : 이것은 초기 어레이를 파괴합니다.)


17

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


2
이것은 n = 5 및 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]에 대해 예상대로 작동하지 않습니다.
Tiago

1
이것은 n 개의 부분 배열로 분할되지 않고 단지 n 개의 길이의 부분 배열로 분할됩니다.
dpg5000

1
이 코드가 OP에 도움이되는 이유에 대한 설명을 추가하십시오. 이는 향후 시청자가 배울 수있는 답을 제공하는 데 도움이 될 것입니다. 자세한 내용 은 응답 방법을 참조하십시오 .
Heretic Monkey

12

알고리즘을 반복적으로 구현했습니다 : 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 2011

7

그것을 행렬로 줄일 수 있습니다. 아래의 예는 배열 ( 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
와우 멋지고 간결한 방법입니다! 그것을 사랑하십시오! 잘 했어! :-)
Philippe Monnet

1
저는이 기술을 좋아하지만 질문에 대한 답이 아닙니다. 어떤 양의 x 크기 청크를 반환하는 반면 질문은 균등 한 크기의 청크 x 양을 요구했습니다.
Jodi Warren

3
이거 너무 좋아 !!! 나는 균일 크기의 덩어리 돌아 리팩토링 한 function splitArr(arr, n) { return arr.reduce(function (a, i) { if (a[a.length - 1].length >= arr.length / n) { a.push([]) } a[a.length - 1].push(i) return a; }, [[]]) }
다비드 andreazzini

3
확실히 질문에 대답하지 않습니다.
macdelacruz

1
간결하고 정말 영리합니다.이 간단한 패턴으로이 문제와 다른 모든 경우를 해결하는 데 선호하는 방법입니다. 감사합니다!
Edmond Tamas

5

업데이트 : 2020 년 7 월 21 일

내가 몇 년 전에 준 대답은 originalArray.length<= 인 경우에만 작동합니다 numCols. 아래의이 기능과 같은 것을 대신 사용할 수 있지만, 당면한 질문과 완전히 일치하지 않는 레이아웃이 생성됩니다 (수직 정렬이 아닌 수평 정렬). AKA : [1,2,3,4]-> [[1,4],[2],[3]]. 나는 이것이 여전히 가치를 제공 할 수 있음을 이해하므로 여기에 남겨 둘 것이지만 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
}

2017 년의 원래 답변 :

오래된 질문이지만 vanillaJS는 요구 사항이 아니기 때문에 많은 사람들이 lodash / chunk로이 문제를 해결하려고 노력하고 있으며 _.chunk실제로 수행 하는 작업을 착각하지 않고 lodash다음 을 사용하는 간결하고 정확한 솔루션이 있습니다 .

(허용되는 답변과 달리 originalArray.length< 하더라도 n 열을 보장합니다. 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끝에 있는 루프는 원하는 "열"수를 보장합니다.


이것은 배열 길이가 4이고 numCols가 3 일 때 실패합니다. splitArray ([1, 2, 3, 4], 3)로 시도하고 [[1, 2], [3, 4], []]를 반환합니다.
Pratik Kulshreshth

@PratikKulshreshth 절대적으로 맞습니다. 답변을 업데이트하겠습니다. 관심이 있으시면 Senthe의 대답이 가장 좋습니다. stackoverflow.com/a/51514813/1322810
Joao

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

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

2
function parseToPages(elements, pageSize = 8) {
    var result = [];
    while (elements.length) {
        result.push(elements.splice(0, pageSize));
    }
    return result;
}

3
대답으로 코드를 덤프하지 마십시오. 이것이 질문의 문제를 어떻게 해결하는지 설명을 추가하십시오.
Mark Rotteveel

2

원하는 청크의 크기를 미리 알고 있다면 매우 우아한 ES6 방법이 있습니다.

const groupsOfFour = ([a,b,c,d, ...etc]) =>
  etc.length? [[a,b,c,d], ...groupsOfFour(etc)] : [[a,b,c,d]];
  
console.log(groupsOfFour([1,2,3,4,1,2,3,4,1,2,3,4]));

이 표기법은 예를 들어 RGBA를 Uint8ClampedArray.


이것이 실패하는 것을 제외하고는 n <4 : 예상 (그리고 원하는) 대신 groupsOfFour( [ 1 ] )반환합니다 . [ 1 , undefined, undefined, undefined ][ [1] ]
Nicholas Carey

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

나는 청크 될 내 자신의 배열을 만들었습니다. 여기 에서 코드를 찾을 수 있습니다.

또한 lodash 라이브러리에는 매우 유용한 메소드 "chunk"가 있습니다. 도움이되는 희망


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

1

일반적으로 돌연변이는 Bad Thing ™입니다.

이것은 훌륭하고 깨끗하며 멱 등성입니다.

function partition(list = [], n = 1) {
  const isPositiveInteger = Number.isSafeInteger(n) && n > 0;
  if (!isPositiveInteger) {
    throw new RangeError('n must be a positive integer');
  }

  const partitions = [];
  const partitionLength = Math.ceil(list.length / n);

  for (let i = 0; i < list.length; i += partitionLength) {
    const partition = list.slice(i, i+partitionLength);
    partitions.push( partition );
  }

  return partitions;
}

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

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

0

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함수형 프로그래밍 접근 방식을 사용할 수 있고 원한다면 다음 과 같습니다.

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(array = [], nPieces = 1){
    const splitArray = [];
    let atArrPos = 0;
    for(let i = 0; i < nPieces; i++){
        const splitArrayLength  = Math.ceil((array.length - atArrPos)/ (nPieces - i));
        splitArray.push([]);
        splitArray[i] = array.slice(atArrPos, splitArrayLength + atArrPos);
        atArrPos += splitArrayLength;
    }
    return  splitArray
}


0

간단한 재귀 함수를 사용할 수 있습니다.

const chunkify = (limit, completeArray, finalArray = [])=>{
    if(!completeArray.length) return finalArray
    const a = completeArray.splice(0,limit);
    return chunkify(limit, completeArray, [...finalArray,a])
}


0
splitToChunks(arrayvar, parts) {
    let result = [];
    for (let i = parts; i > 0; i--) {
        result.push(arrayvar.splice(0, Math.ceil(arrayvar.length / i)));
    }
    return result;
}

-1

lodash의 chunk 함수를 사용하여 배열을 더 작은 배열로 분할하십시오. https://lodash.com/docs#chunk 더 이상 루프를 조작 할 필요가 없습니다!


1
질문은 js 라이브러리를 사용하지 않고 vannila js를 사용하여이 문제를 해결하는 방법을 묻습니다
TJ

3
알려 주셔서 감사합니다. 나는 lodash가 이것을 가지고 있는지 몰랐습니다.
Tiago

9
이것은 또한 질문에 대답하지 않습니다. 그는 N 개 요소의 배열이 아닌 N 개의 배열을 원합니다.
mAAdhaTTah

-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
이것은 잘못된 것입니다. _.chunkN 배열이 아닌 N 요소의 배열을 만듭니다. 당신의 예제는 마지막 하나를 제외하고 각각 2 개의 요소를 가진 6 개의 배열의 출력을 가질 것입니다[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]]
Vassilis Barzokas

그게 원래 질문입니다. 질문에서 예상되는 동작을 읽으십시오.
아비 섹폴
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.