Как разделить массив по пределу памяти? - PullRequest
0 голосов
/ 02 апреля 2020

Я хочу разбить массив объектов на порции с ограничением памяти, то есть каждый порции не должен превышать 4 МБ, есть ли способ сделать это? С помощью приведенного ниже кода я получил размер входного массива.

var sizeof = require('object-sizeof')

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

console.log(sizeof(arr))

1 Ответ

0 голосов
/ 02 апреля 2020

Если вы хотите ограничить данные, то вам нужно определить свой алгоритм разбиения на блоки для работы в пределах квоты . Шаги на самом деле довольно просты:

  1. Создайте новый чанк.
  2. Добавляйте в него элементы до тех пор, пока квота не будет заполнена - НЕ превышайте его.
  3. Если квота достигнута, начиная с 1.
  4. Если достигнут конец массива - fini sh.

Вот как может выглядеть реализация.

ПРИМЕЧАНИЕ : Я верю, что библиотека object-sizeof правильно подсчитывает размер объектов. Я не уверен, что ссылки на объекты действительно должны быть приняты во внимание. Итак, при условии правильной реализации .

ПРИМЕЧАНИЕ 2 : я не смог найти копию библиотеки CDN, поэтому я реализовал очень тупой алгоритм замены для пример целей. Каждый ключ имеет размер 1, а каждое значение имеет значение 1. Значения объектов вычисляются рекурсивно.

/*
 * dumb implementation of `sizeof` for example purposes.
 * The "size" is 1 for each key and 1 for each simple value.
 *  {a: "b"} has size = 2
 *  {a: {b: "c"}} has size = 3, etc.
 */
var sizeof = obj => Object.entries(obj)
  .reduce((sum, [key, value]) => 
    sum + 1 + (typeof value !== "object" ? 1 : sizeof(value)),
    0
  )

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

function chunkToLimit(arr, limit) {
  var result = [];
  
  //variables needed for the loop. Initialised properly later
  var chunk;
  var remainingQuota = -Infinity;
  
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    var size = sizeof(item);

    if (size > remainingQuota) {
      //the current chunk that will be filled
      chunk = [];
      //account for the size of the empty chunk itself
      remainingQuota = limit - sizeof(chunk);
      
      //add to result
      result.push(chunk);
    }
    
    remainingQuota -= size
    chunk.push(item);
  }
  
  return result;
}

console.log(chunkToLimit(arr, 10))

Это будет учитывать ограничение памяти , но если вам нужно отправить это через inte rnet, тогда вы будете использовать JSON сериализация и, следовательно, ваша полезная нагрузка может иметь совершенно другой размер, чем обычный объект, поскольку каждый элемент будет закодирован в строку и, более того, {a: "b", c:"d"} даже не будет иметь размер "a" + "b" + "c" + "d", но также включает {, }, обозначающие объект, ,, разделяющие свойства, а также ", окружающие каждый ключ и значение. Таким образом, размер будет отличаться от размера объекта.

К счастью, размер JSON гораздо проще вычислить. Это строка, поэтому вам просто нужно знать ее размер. И для этого вы можете использовать Buffer в Node.js. Buffer.byteLength может использоваться для прямого вычисления, вычисляет размер байта объекта при сериализации в JSON.

var sizeof = obj =>  Buffer.byteLength(JSON.stringify(obj), 'utf8')

В этом случае вам потребуются другие шаги, чем прежде:

  1. Проверьте, не превысит ли порция ограничение при добавлении нового элемента.
  2. Если это так, завершите работу с текущим порцией и начните новый.
  3. Добавить текущий элемент к чану.
  4. Повторяйте, пока массив не будет исчерпан.

Вот реализация:

NOTE : I ' m, используя Blob, поскольку он доступен в браузере. Операция должна быть идентичной, изменяется только функция sizeof.

var sizeof = obj => new Blob([JSON.stringify(obj)], {type : 'application/json'}).size;

var arr = [{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"g1ob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"},{test:"gob", gg:"2"}];

function chunkToLimit(arr, limit) {
  //variables needed for the loop
  
  //start first chunk
  var chunk = [];
  //add it to the array
  var result = [chunk];
  var size;
  
  for (var i = 0; i < arr.length; i++) {
    var item = arr[i];
    //concat in order to not modify the chunk and do a check before actually adding
    size = sizeof(chunk.concat(item))

    //check if the limit would be exceeded
    if (size > limit) {
      //if so, start a new chunk
      
      chunk = [];
      result.push(chunk);
    }
    
    //add item to chunk
    chunk.push(item);
  }
  
  return result;
}

var result = chunkToLimit(arr, 60)

for (var chunk of result) {
  var prettyPrint = `${JSON.stringify(chunk)}
  size: ${sizeof(chunk)}`;
  
  console.log(prettyPrint);
}
...