Skip to content

Commit

Permalink
feat: remove array items without consuming extra memory (#59)
Browse files Browse the repository at this point in the history
* feat: add remove-array-items that can remove items from an array without generating memory garbage

* chore: replace arr.splice(start, deleteCount) with removeArrayItems(arr, start, deleteCount)
  • Loading branch information
cheton authored Nov 21, 2021
1 parent 8dcd28e commit a4375cb
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 8 deletions.
17 changes: 9 additions & 8 deletions src/infinite-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { flatten, Node } from 'flattree';
import Clusterize from './clusterize';
import ensureArray from './ensure-array';
import extend from './extend';
import { get } from './utilities';
import LookupTable from './lookup-table';
import removeArrayItems from './remove-array-items';
import { defaultRowRenderer } from './renderer';
import { get } from './utilities';
import {
preventDefault,
addEventListener,
Expand Down Expand Up @@ -749,8 +750,8 @@ class InfiniteTree extends events.EventEmitter {
}

// Update nodes & rows
this.nodes.splice(nodeIndex + 1, total);
this.rows.splice(nodeIndex + 1, total);
removeArrayItems(this.nodes, nodeIndex + 1, total);
removeArrayItems(this.rows, nodeIndex + 1, total);

// Toggle the collapsing state
node.state.collapsing = false;
Expand Down Expand Up @@ -1361,8 +1362,8 @@ class InfiniteTree extends events.EventEmitter {

if (parentNodeIndex >= 0) {
// Update nodes & rows
this.nodes.splice(parentNodeIndex + 1, deleteCount);
this.rows.splice(parentNodeIndex + 1, deleteCount);
removeArrayItems(this.nodes, parentNodeIndex + 1, deleteCount);
removeArrayItems(this.rows, parentNodeIndex + 1, deleteCount);

// Update the row corresponding to the parent node
this.rows[parentNodeIndex] = this.options.rowRenderer(parentNode, this.options);
Expand Down Expand Up @@ -1441,15 +1442,15 @@ class InfiniteTree extends events.EventEmitter {
}

// Update parent node
parentNode.children.splice(parentNode.children.indexOf(node), 1);
removeArrayItems(parentNode.children, parentNode.children.indexOf(node), 1);
if (parentNode !== this.state.rootNode) {
parentNode.state.open = parentNode.state.open && (parentNode.children.length > 0);
}

if (nodeIndex >= 0) {
// Update nodes & rows
this.nodes.splice(nodeIndex, deleteCount);
this.rows.splice(nodeIndex, deleteCount);
removeArrayItems(this.nodes, nodeIndex, deleteCount);
removeArrayItems(this.rows, nodeIndex, deleteCount);
}

// Update the row corresponding to the parent node
Expand Down
27 changes: 27 additions & 0 deletions src/remove-array-items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Remove a range of items from an array.
*
* @function removeItems
* @param {Array<*>} arr The target array.
* @param {number} startIndex The index to begin removing from (inclusive).
* @param {number} removeCount How many items to remove.
*/
const removeArrayItems = (arr, startIndex, removeCount) => {
const length = arr.length;

if (startIndex >= length || removeCount <= 0 || startIndex < 0) {
return;
}

removeCount = (startIndex + removeCount > length ? length - startIndex : removeCount);

const len = length - removeCount;

for (let i = startIndex; i < len; ++i) {
arr[i] = arr[i + removeCount];
}

arr.length = len;
};

export default removeArrayItems;
51 changes: 51 additions & 0 deletions test/remove-array-items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { test } from 'tap';
import removeArrayItems from '../src/remove-array-items';

test('should return if the start index is greater than or equal to the length of the array', (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, arr.length + 1, 5);
t.equals(arr.length, 10);
t.end();
})

test('should return if the remove count is 0', (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, 2, 0);
t.equals(arr.length, 10);
t.end();
});

test('should remove the number of elements specified from the array, starting from the start index', (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, 3, 4);
t.deepEquals(arr, [1, 2, 3, 8, 9, 10]);
t.end();
});

test('should remove other elements if delete count is larger than the number of elements after start index', (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, 7, 10);
t.deepEquals(arr, [1, 2, 3, 4, 5, 6, 7]);
t.end();
});

test('should remove no element if count is less than or equal to zero', function (t) {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, 7, -2);
t.deepEquals(arr, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
t.end();
});

test('should remove no element if start is less than or equal to zero', (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, -7, 5);
t.deepEquals(arr, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
t.end();
});

test("should remove the remaining elements start with 'start' if count is greater than arr.length", (t) => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
removeArrayItems(arr, 4, 100);
t.deepEquals(arr, [1, 2, 3, 4]);
t.end();
});

0 comments on commit a4375cb

Please sign in to comment.