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]]]、そして私はヌルセパレーターでそれをすることに決めました)。

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機能はそれほど遠くありません。null2つの配列ラッパーを追加することでビジネスを削除できます:if (cols == 1) return [array]return [array.slice(0, size)].concat(split(array.slice(size), cols-1))。この再帰バージョンは、ここにあるほとんどの回答よりもはるかに読みやすいと思います。
ScottSauyet18年

回答:


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>


あなたの解決策はきちんとしていて、私の再帰的な解決策と同じことをしますが、そのすべての混乱はありません。ありがとうございました!
ティアゴ

3
魅力のように機能します..素晴らしい解決策
Vardan 2014

こんにちは@georg、この行を説明していただけますか: var size = Math.ceil((len - i) / n--);
dpg5000 2016

@ dpg5000:次のチャンクをスライスするとき、その大きさは、残りの要素の数である(len - i残りのチャンクの数(による)n--
ゲオルグ

1
こんにちは@georgありがとうございます。このコードを変更して、最後のサブ配列を除いてすべてのサブ配列の長さが等しくなるようにするにはどうすればよいですか(もちろん、除数の余りがなく、すべてのサブ配列が等しくなる場合を除きます)。私はどんな助けにも感謝します。
dpg5000 2016

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 = 3、の場合、残りの部分の1/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]では期待どおりに機能しません。
ティアゴ2014

1
これはn個のサブ配列に分割されず、単にn個の長さのサブ配列に分割されます。
dpg5000 2016

1
このコードがOPに役立つ理由の説明を追加してください。これは、将来の視聴者が学ぶことができる答えを提供するのに役立ちます。詳細については、回答方法を参照してください。
異端の猿

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
(これは期待どおりに機能しますが、正解は1つしか選択できません)こんにちは。助けてくれてありがとう。残りの使い方を考えてみてください。
ティアゴ

7

あなたはそれを行列に減らすことができます。以下の例では、配列(arr)を2位置配列の行列に分割します。他のサイズが必要な場合は、2行目の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
これを行うためのすごい簡潔な方法!大好きです!よくやった!:-)
フィリップモネ2015

1
私はこのテクニックが大好きですが、それは質問に答えていません。それは任意の量のxサイズのチャンクを返しますが、質問はx量の均等なサイズのチャンクを求めていました。
ジョディウォーレン

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

3
間違いなく質問に答えません。
macdelacruz 2017年

1
簡潔で本当に賢い、この単純なパターンでこれと他のすべてのケースを解決するための私の好ましい方法、ありがとう!
エドモンドタマス

5

更新:2020年7月21日

私が数年前に与えた答えは、次の場合にのみ機能します originalArray.length <=のnumColsます。または、以下のこの関数のようなものを使用することもできますが、それでは、目前の質問と完全には一致しないレイアウトが作成されます(垂直方向の並べ替えではなく水平方向の並べ替え)。別名:[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ます。

(受け入れられた回答とは異なり、これは次の場合でも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最後のループは、必要な数の「列」を保証するものです。


配列の長さが4で、numColsが3の場合、これは失敗します。splitArray([1、2、3、4]、3)で試行すると、[[1、2]、[3、4]、[]]が返されます。
PratikKulshreshth20年

あなたは絶対に正しい@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 2018

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] ]
ニコラスキャリー

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ライブラリには非常に便利なメソッド「チャンク」があります。お役に立てば幸いです


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

突然変異は、一般的に言えば、BadThing™です。

これは素晴らしく、クリーンで、べき等です。

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のチャンク関数を使用して配列を小さな配列に分割するだけですhttps://lodash.com/docs#chunkループをいじる必要はもうありません!


1
質問は、jsライブラリを使用せずにvannila jsを使用してこれを解決する方法を尋ねます
TJ

3
これを持ってきてくれてありがとう。lodashがこれを持っているとは知りませんでした。
ティアゴ

9
これも質問に答えません。彼は、N個の要素の配列ではなく、N個の配列を必要としています。
mAAdhaTTah 2016

-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個の要素の配列を作成します。あなたの例では、最後の1つを[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]]
除いて

それが元の質問です。質問で予想される動作をお読みください。
abhisekpaul 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.