Разделить массив JavaScript на куски с помощью Lodash


83

Мне нужно разбить массив JavaScript на nкуски определенного размера.

Например: учитывая этот массив

["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"]

и a nравно 4, результат должен быть таким:

[ ["a1", "a2", "a3", "a4"],
  ["a5", "a6", "a7", "a8"],
  ["a9", "a10", "a11", "a12"],
  ["a13"]
]

Мне известны решения этой проблемы на чистом JavaScript , но, поскольку я уже использую Lodash, мне интересно, предоставляет ли Lodash лучшее решение для этой проблемы.

Редактировать:

Я создал тест jsPerf, чтобы проверить, насколько медленнее решение для подчеркивания.

Ответы:


126

Взгляните на кусок lodash : https://lodash.com/docs#chunk

const data = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"];
const chunks = _.chunk(data, 3);
console.log(chunks);
// [
//  ["a1", "a2", "a3"],
//  ["a4", "a5", "a6"],
//  ["a7", "a8", "a9"],
//  ["a10", "a11", "a12"],
//  ["a13"]
// ]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


Было бы полезно, если бы lodash и подчеркивали оба обратно совместимых API
Jonno_FTW

@Jonah Это правда. Предыдущий ответ уже не является лучшим, поэтому я обновил принятый ответ ради читателей :)
Сезар Канасса

90

Для решения на основе Underscore попробуйте следующее:

var data = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"];
var n = 3;
var lists = _.groupBy(data, function(element, index){
  return Math.floor(index/n);
});
lists = _.toArray(lists); //Added this to convert the returned object to an array.
console.log(lists);

Используя метод цепной оболочки, вы можете объединить два оператора, как показано ниже:

var data = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"];
var n = 3;
var lists = _.chain(data).groupBy(function(element, index){
  return Math.floor(index/n);
}).toArray()
.value();

1
Как ни странно, Underscore groupBy возвращает объект , а не массив. Это не нарушитель договоренностей - вы получаете в основном те же функции, но немного странной семантики. Изменить: О, он делает это для поддержания цепочки. Все еще интересно.
Jordan Running

@Cesar Canassa: Ради будущих разработчиков, которые читают этот код, подумайте о том, чтобы добавить это как функцию в пользовательскую библиотеку, если вы будете ее
часто

@Jordan: Не осознавал этого. Отредактировал ответ, чтобы преобразовать объект в массив.
Chandu

Хотя я за то, чтобы использовать Underscore для того, что он делает лучше всего (и OP запросил решение на основе Underscore, чтобы не было незаслуженным) и для функциональных решений, похоже, что это решение вводит массу накладных расходов. Как указывает @ ajax33321, spliceцикл по-прежнему является трехстрочным и, как я предполагаю, намного более производительным. Если ваши массивы короткие, это, вероятно, не имеет значения, но если вы имеете дело с сотнями или тысячами элементов, следите за их производительностью.
Jordan Running

1
Это уже не лучшее решение. Пожалуйста, используйте вместо него chunk ()
Иона

4

Возможно более простое выражение:

const coll = [ "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9" ];
const n = 2;
const chunks = _.range(coll.length / n).map(i => coll.slice(i * n, (i + 1) * n));
console.log(chunks);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>


1
Проще чем что? Ответы должны стоять отдельно.
Натан Тагги

4

Underscore изначально поддерживает _.chunk () начиная с версии 1.9.0.

const data = ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13"];
const chunks = _.chunk(data, 4);
console.log(chunks);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore.js"></script>


1

попробуйте этот вариант, это гораздо практичнее (например, если вы хотите разделить массив на основе количества элементов, которые будут контейнером в каждом подмассиве):

function chunk(arr, start, amount){
    var result = [], 
        i, 
        start = start || 0, 
        amount = amount || 500, 
        len = arr.length;

    do {
        //console.log('appending ', start, '-', start + amount, 'of ', len, '.');
        result.push(arr.slice(start, start+amount));
        start += amount;

    } while (start< len);

    return result;
};

и использование в вашем случае:

var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],
    chunked = chunk(arr, 0, Math.floor(arr.length/3)); //to get 4 nested arrays

console.log(chunked);

и еще один случай:

var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],
    chunked = chunk(arr, 0, 3); // to get 6 nested arrays each containing maximum of 3 items

console.log(chunked);

Этот startпараметр не очень практичен, imho. Обычно вы не хотите его проходить; но вам всегда нужно указывать размер блока. Лучше поменять местами эти два параметра.
Берги

Почему это do whileпетля? Никто не хочет получать пустые куски.
Берги
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.