├── .github └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src └── remove-array-items.js └── test └── index.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | # This workflow contains a single job called "build" 17 | build: 18 | # The type of runner that the job will run on 19 | runs-on: ubuntu-latest 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v2 25 | 26 | # Runs a single command using the runners shell 27 | - name: setup 28 | run: npm install 29 | 30 | # Runs a single command using the runners shell 31 | - name: tests 32 | run: npm test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | *.log 4 | node_modules 5 | .env* 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | engine-strict=true 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.0.0 2 | * BREAKING: move to a pure es module, drop commonjs and the build step 3 | * simplify usage instructions 4 | * update tap and tests to use esm 5 | * remove node 6-11 from travis tests 6 | 7 | 8 | ## 2.0.0 9 | * remove node 4, 5 from travis tests 10 | * move source code into src/ and convert to es6 11 | * remove unneeded 'use strict' 12 | 13 | 14 | ## 1.1.0 15 | * support es6 modules in rollup and webpack via package.json module field 16 | * update deps 17 | * update travis config 18 | 19 | 20 | ## 1.0.0 21 | 22 | * initial commit 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mike Reinstein 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # remove-array-items 2 | 3 | remove items from a javascript array without generating memory garbage. 4 | 5 | ![tests](https://github.com/mreinstein/remove-array-items/actions/workflows/main.yml/badge.svg) 6 | 7 | 8 | Despite there being a number of "remove array items" in npm, I couldn't find satisfying all criteria: 9 | 10 | * **doesn't generate garbage** 11 | * **performs similar or better to the native `array.splice`** 12 | * has tests 13 | * is a pure es module 14 | 15 | so here we are. 16 | 17 | originally inspired by https://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/ 18 | 19 | (which is a gold mine for performant, non-garbage generating array operations by the way.) 20 | 21 | 22 | ## usage 23 | 24 | ```javascript 25 | import removeItems from 'remove-array-items' 26 | 27 | 28 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] 29 | 30 | const startIdx = 3 // integer >= 0 31 | const removeCount = 4 // int >= 0 32 | 33 | removeItems(arr, startIdx, removeCount) // after running, arr === [ 1, 2, 3, 8, 9 ] 34 | ``` 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remove-array-items", 3 | "version": "3.0.0", 4 | "description": "remove items from a javascript array without generating memory garbage", 5 | "main": "src/remove-array-items.js", 6 | "type": "module", 7 | "scripts": { 8 | "prepublishOnly": "npm test", 9 | "test": "tap --no-esm ./test/" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/mreinstein/remove-array-items.git" 14 | }, 15 | "keywords": [ 16 | "array", 17 | "splice", 18 | "remove", 19 | "nogarbage", 20 | "gcfriendly" 21 | ], 22 | "author": "Mike Reinstein", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/mreinstein/remove-array-items/issues" 26 | }, 27 | "homepage": "https://github.com/mreinstein/remove-array-items#readme", 28 | "devDependencies": { 29 | "tap": "^14.11.0" 30 | }, 31 | "engines": { 32 | "node": ">=12.17" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/remove-array-items.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove a range of items from an array 3 | * 4 | * @function removeItems 5 | * @param {Array<*>} arr The target array 6 | * @param {number} startIdx The index to begin removing from (inclusive) 7 | * @param {number} removeCount How many items to remove 8 | */ 9 | export default function removeItems (arr, startIdx, removeCount) { 10 | const length = arr.length 11 | 12 | if (startIdx >= length || removeCount <= 0 || startIdx < 0) 13 | return 14 | 15 | removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount) 16 | 17 | const len = length - removeCount 18 | 19 | for (let i = startIdx; i < len; ++i) 20 | arr[i] = arr[i + removeCount] 21 | 22 | arr.length = len 23 | } 24 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import removeItems from '../src/remove-array-items.js' 2 | import tap from 'tap' 3 | 4 | 5 | tap.test('should return if the start index is greater than or equal to the length of the array', function (t) { 6 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 7 | removeItems(arr, arr.length + 1, 5) 8 | t.equals(arr.length, 10) 9 | t.end() 10 | }) 11 | 12 | 13 | tap.test('should return if the remove count is 0', function (t) { 14 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 15 | removeItems(arr, 2, 0) 16 | t.equals(arr.length, 10) 17 | t.end() 18 | }) 19 | 20 | tap.test('should remove the number of elements specified from the array, starting from the start index', function (t) { 21 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 22 | removeItems(arr, 3, 4) 23 | t.deepEquals(arr, [ 1, 2, 3, 8, 9, 10 ]) 24 | t.end() 25 | }) 26 | 27 | tap.test('should remove other elements if delete count is > than the number of elements after start index', function (t) { 28 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 29 | removeItems(arr, 7, 10) 30 | t.deepEquals(arr, [ 1, 2, 3, 4, 5, 6, 7 ]) 31 | t.end() 32 | }) 33 | 34 | tap.test('should remove no element if count <=0', function (t) { 35 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 36 | removeItems(arr, 7, -2) 37 | t.deepEquals(arr, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) 38 | t.end() 39 | }) 40 | 41 | tap.test('should remove no element if start <=0', function (t) { 42 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 43 | removeItems(arr, -7, 5) 44 | t.deepEquals(arr, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]) 45 | t.end() 46 | }) 47 | 48 | tap.test("should remove the remaining elements start with 'start' if count > arr.length", function (t) { 49 | const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 50 | removeItems(arr, 4, 100) 51 | t.deepEquals(arr, [ 1, 2, 3, 4]) 52 | t.end() 53 | }) 54 | --------------------------------------------------------------------------------