如何将数组分割成块?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (10)
  • 关注 (0)
  • 查看 (51)

假设我有一个Javascript数组,如下所示:

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

当最多有10个元素的时候,什么样的方法适合于将数组块(分割)成许多较小的数组?

提问于
用户回答回答于

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
}
用户回答回答于

如果使用版本高于或者等于5.1的ECMAScript,则可以使用具有O(N)复杂性的array.reduce()来实现chunk():

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' ] ]
用户回答回答于
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);
用户回答回答于
用户回答回答于

我把不同的答案测试到jsper.com上,调查结果:http://jsper.com/chunk-MTDS

最快的Functio(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;
}
用户回答回答于

ES6版本:

X = 2; // items per chunk    

a = ['a','b','c','d'];

a.reduce((ar, it, i) => { 
  const ix = Math.floor(i/X); 

  if(!ar[ix]) {
    ar[ix] = [];
  }

  ar[ix].push(it);

  return ar;
}, [])

// result: [["a","b"], ["c","d"]]

你可以将它链入到进一步的映射/减少转换中,你的输入数组保持完整。

用户回答回答于

splice方法:

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
用户回答回答于
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]]
用户回答回答于

尽量避免使用本机原型,包括Array.prototype

有许多方法可以安全地扩展原型(但不是在所有浏览器中),也有方法可以安全地使用从扩展原型创建的对象,但更好的经验法则是遵循Principle of Least Surprise来避免这些做法。

扩展内置

虽然上面的解决方案会有效,但它们过于复杂,并且需要不必要的计算开销。以下为我的解决方案:

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;
用户回答回答于

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

演示:

> [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]]

它与下面的方法具有相同的渐近运行时间,但可能是由于构建空列表而导致的一个更糟糕的常数因子。我们可以将其改写为以下的代码:

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;
    }
});
// refresh page if experimenting and you already defined Array.prototype.chunk
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函数,那么实际上它只有一行(不包括fluff):

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

扫码关注云+社区