{"id":21140544,"url":"https://github.com/intoli/slice","last_synced_at":"2025-07-09T04:31:29.185Z","repository":{"id":44175951,"uuid":"135637727","full_name":"intoli/slice","owner":"intoli","description":"A JavaScript implementation of Python's negative indexing and extended slice syntax.","archived":false,"fork":false,"pushed_at":"2022-12-03T23:38:16.000Z","size":829,"stargazers_count":55,"open_issues_count":15,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-24T16:52:46.435Z","etag":null,"topics":["arrays","javascript","negative-indices","nodejs","python","range","slice","slicing","strings"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/intoli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-31T21:37:36.000Z","updated_at":"2024-03-10T13:37:01.000Z","dependencies_parsed_at":"2023-01-23T23:04:48.318Z","dependency_job_id":null,"html_url":"https://github.com/intoli/slice","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/intoli/slice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intoli%2Fslice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intoli%2Fslice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intoli%2Fslice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intoli%2Fslice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/intoli","download_url":"https://codeload.github.com/intoli/slice/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intoli%2Fslice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263774042,"owners_count":23509293,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arrays","javascript","negative-indices","nodejs","python","range","slice","slicing","strings"],"created_at":"2024-11-20T07:16:40.961Z","updated_at":"2025-07-09T04:31:28.849Z","avatar_url":"https://github.com/intoli.png","language":"JavaScript","readme":"\u003ch1 vertical-align=\"middle\"\u003e\n    \u003cimg height=\"100px\" src=\"media/logo.svg\" alt=\"Slice\"\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"left\"\u003e\n    \u003ca href=\"https://circleci.com/gh/intoli/slice/tree/master\"\u003e\n        \u003cimg src=\"https://img.shields.io/circleci/project/github/intoli/slice/master.svg\"\n            alt=\"Build Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/intoli/slice/blob/master/LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/License-BSD%202--Clause-blue.svg\"\n            alt=\"License\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://www.npmjs.com/package/slice\"\u003e\n        \u003cimg src=\"https://img.shields.io/npm/v/slice.svg\"\n            alt=\"NPM Version\"\u003e\u003c/a\u003e\n    \u003cspan\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003c/span\u003e\n    \u003ca target=\"_blank\" href=\"https://twitter.com/home?status=Slice%20%E2%80%94%20A%20Javascript%20implementation%20of%20Python's%20awesome%20negative%20indexing%20and%20extended%20slice%20syntax%20%40IntoliNow%20%23Intoli%0A%0Ahttps%3A//github.com/intoli/slice\"\u003e\n        \u003cimg height=\"26px\" src=\"https://simplesharebuttons.com/images/somacro/twitter.png\"\n            alt=\"Tweet\"\u003e\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.facebook.com/sharer/sharer.php?u=https%3A//github.com/intoli/slice\"\u003e\n        \u003cimg height=\"26px\" src=\"https://simplesharebuttons.com/images/somacro/facebook.png\"\n            alt=\"Share on Facebook\"\u003e\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"http://reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Fintoli%2Fslice\u0026title=Slice%20%E2%80%94%20A%20Javascript%20implementation%20of%20Python's%20awesome%20negative%20indexing%20and%20extended%20slice%20syntax\"\u003e\n        \u003cimg height=\"26px\" src=\"https://simplesharebuttons.com/images/somacro/reddit.png\"\n            alt=\"Share on Reddit\"\u003e\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://news.ycombinator.com/submitlink?u=https://github.com/intoli/slice\u0026t=Slice%20%E2%80%94%20Python's%20negative%20indexing%20and%20extended%20slice%20syntax%20for%20JavaScript\"\u003e\n        \u003cimg height=\"26px\" src=\"media/ycombinator.png\"\n            alt=\"Share on Hacker News\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n###### [For Python Programmers](#for-people-who-know-python-already) | [Installation](#installation) | [API](#api) | [Contributing](#contributing)\n\n\u003e Slice is a JavaScript implementation of Python's awesome negative indexing and [extended slice](https://docs.python.org/2.3/whatsnew/section-slices.html) syntax for arrays and strings.\n\u003e It uses ES6 proxies to allow for an intuitive double-bracket indexing syntax which closely replicates how slices are constructed in Python.\n\u003e Oh, and it comes with an implementation of Python's [range](https://docs.python.org/3/library/stdtypes.html#typesseq-range) method too!\n\n\nIf you know Python, then you're probably well aware of how pleasant Python's indexing and slice syntax make working with lists and strings (*and you can skip ahead to [For People Who Know Python Already](#for-people-who-know-python-already) if you want*).\nIf not—well, you're in for a treat!\nSlice adds `SliceArray` and `SliceString` classes which extend the corresponding builtin types to provide a unified and concise syntax for indexing and slicing in JavaScript.\n\nFor starters, negative indices can be used to count backwards from the end of an array or string.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n// Outputs: 4\narray[-1]\n\nconst string = SliceString('Hello World!');\n// Outputs: 'd'\nstring[-2]\n```\n\nThat's a convenient alternative to needing to write things like `array[array.length - n]`, but it's really just the beginning of what Slice has to offer.\nSlice also introduces a double bracket indexing syntax which allows you to specify subranges of iterables by writing `array[[start,stop]]`.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n// Outputs: [2, 3]\narray[[1,-1]]\n```\n\nThis is functionally identical to the builtin [Array.slice()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice) method, but it also works for strings *and* it supports assignment using the same interface.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\narray[[1,-1]] = ['two', 'three', 'three and a half'];\n// Outputs: [1, 'two', 'three', 'three and half', 4]\narray\n```\n\nIt's also possible to leave off either the `start` or `stop` parameter to have the range automatically extend to the beginning or end of the iterable.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n// Outputs: [2, 3, 4]\narray[[1,]]\n// Outputs: [1, 2, 3]\narray[[,-1]]\n\nconst string = SliceString('Hello World!');\n// Outputs: 'World!'\nstring[[6,]]\n```\n\nYou can also add a third `step` parameter to your slices using the `array[[start,stop,step]]` syntax.\nThat's when things get really interesting.\nThe `step` parameter allows you to easily extract every Nth element from an iterable while optionally specifying a subrange at the same time.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n\n// Outputs: [1, 3]\narray[[,,2]]\n\n// Outputs: [2, 4]\narray[[1,,2]]\n```\n\nAnd, of course, extended slices also support assignment!\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n\narray[[,,2]] = ['odd', 'odd'];\n// Outputs: ['odd', 2, 'odd', 4]\narray\n\n[array[[,,2]], array[[1,,2]]] = [array[[1,,2]], array[[,,2]]];\n// Outputs: [2, 'odd', 4, 'odd']\narray\n```\n\nYou can even use negative values for the `step` parameter to iterate backwards through an array or string.\n\n```javascript\nconst array = SliceArray(1, 2, 3, 4);\n\n// Outputs: [4, 3, 2, 1]\narray[[,,-1]]\n\n// Outputs: [4, 2]\narray[[,,-2]]\n```\n\nLet's put this together into one last example that's a little more fun.\nWe'll use Slice's extended slice syntax and it's `range()` function to solve [Fizz Buzz](http://wiki.c2.com/?FizzBuzzTest) without any explicit loops or recursion.\n\n```javascript\nimport { range, SliceArray } from 'slice';\n\n\n// Populate a list from 1 through 100.\nconst outputs = range(1, 100 + 1);\n\n// Replace every 3rd element with 'Fizz'.\noutputs[[3 - 1,,3]] =\n  Array(Math.floor(100 / 3))\n    .fill('Fizz');\n\n// Replace every 5th element with 'Buzz'.\noutputs[[5 - 1,,5]] =\n  Array(Math.floor(100 / 5))\n    .fill('Buzz');\n\n// Replace every (3 * 5)th element with 'Fizz Buzz'.\noutputs[[3 * 5 - 1,,3 * 5]] =\n  Array(Math.floor(100 / (3 * 5)))\n    .fill('Fizz Buzz');\n\n// Tada!\nconsole.log(outputs);\n```\n\nIf you're ready to give it a try, then head over to the [installation section](#installation) or take a look at the [API documentation](#api).\nYou also might find the [For People Who Know Python Already](#for-people-who-know-python-already) section interesting, even if you've never used Python before.\nIt provides some context for why this library exists and works the way that it does.\n\n\n## For People Who Know Python Already\n\nIf you know Python already, then you'll be right at home with Slice.\nThe methods and syntax that it introduces are designed to *very* closely mirror those from Python.\nPython includes two built-in functions that Slice provides analogues of: [range()](https://docs.python.org/3/library/functions.html#func-range) and [slice()](https://docs.python.org/3/library/functions.html#slice).\nThe method signatures of these methods are identical to those used in Python, and the behavior and usage of them is very similar.\n\nOne major difference is that `range()` produces an iterator in Python while it produces a fully populated `SliceArray` in JavaScript, similar to how `range()` worked in Python 2.\nThis choice was made because Python has built-in support for its slice syntax, but JavaScript requires subclassing [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) and [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) in order to add support for a similar syntax.\nThe `range()` method returns a `SliceArray` so that the return value immediately supports slicing for convenience.\n\nFor example, you could run the following without needing to explicitly construct a `SliceArray`.\n\n```javascript\nimport { range, slice } from 'slice';\n\n// Outputs: [10, 11, 12, 13, 14]\nrange(100)[slice(10, 15)]\n```\n\nAside from the imports, the actual usage of `range()` and `slice()` here is also valid Python and would produce the same result.\nEven if you use Python quite a bit, however, there's a good chance that you might not that familiar with the explicit usage of `slice()` like this.\nThat's because it's way more common to use Python's slice syntax rather than manually instantiating the `slice` class.\n\n```python\n# These are both equivalent in Python.\nrange(100)[slice(10, 15)]\nrange(100)[10:15]\n```\n\nIt's not possible to replicate that exact syntax in JavaScript, but Slice uses a very similar syntax that should be immediately familiar to you if you know Python.\nAll you need to do is to use double brackets for the indexing and to replace the colons with commas.\nThe slicing will work exactly as you would expect in Python after that.\nIt supports negative indexing, empty parameters, extended slices, negative steps, assignment to slices, and the whole shebang.\nIn fact, part of the test suite actually [runs a Python script](test/generate_tests.py) to perform thousands of slicing operations to verify that the JavaScript results are identical!\n\nHere are a few examples of how the syntax compares between Python and Slice in JavaScript.\n\n| Input             | Python Code      | JavaScript Code    | Output            |\n|-------------------|------------------|--------------------|-------------------|\n| `[0, 1, 2, 3, 4]` | `array[-2]`      | `array[[-2]]`      | `3`               |\n| `[0, 1, 2, 3, 4]` | `array[:2]`      | `array[[,2]]`      | `[0, 1]`          |\n| `[0, 1, 2, 3, 4]` | `array[1::2]`    | `array[[1,,2]]`    | `[1, 3]`          |\n| `[0, 1, 2, 3, 4]` | `array[::-1]`    | `array[[,,-1]]`    | `[4, 3, 2, 1, 0]` |\n| `'hello world'`   | `string[::-1]`   | `string[[,,-1]]`   | `'dlrow olleh'`   |\n| `'hello world'`   | `string[1:-1]`   | `string[[1,-1]]`   | `'ello worl'`     |\n| `'hello world'`   | `string[1:-1:2]` | `string[[1,-1,2]]` | `'el o l'`        |\n| `'hello world'`   | `string[:-5]`    | `string[[,-5]]`    | `'world'`         |\n\nOnce you get used to how the Python syntax maps to the double bracket syntax, it becomes quite easy to switch seamlessly between the two.\n\nWe've looked already at how `range()` can be used to constuct slice-able arrays; the one other thing you need to know is how to construct `SliceArray` and `SliceString` instances manually.\nThese classes have identical interfaces into JavaScript's built-in [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) and [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) objects.\nThey can be constructed in exactly the same ways and are essentially drop-in replacements for `Array` and `String`.\n\n```javascript\nimport { range, SliceArray, SliceString } from 'slice';\n\n// All of the following are equivalent.\nrange(5);\nSliceArray(0, 1, 2, 3, 4);\nnew SliceArray(0, 1, 2, 3, 4);\nSliceArray.from([0, 1, 2, 3, 4]);\n\n// The following are also equivalent.\nSliceString('hello world');\nnew SliceString('hello world');\n```\n\nThey also support all of the same methods once constructed, but will return slice-able arrays and strings whenever possible.\nFor example, you can do things like this without needing to worry about converting the method outputs to slice-able objects.\n\n```javascript\nconst helloWorld = SliceString('hello world');\n// Outputs: 'DLROW OLLEH'\nhelloWorld.toUpperCase()[[,,-1]];\n\n// Outputs: [1, 4, 6]\nrange(5).map(i =\u003e i * 2)[[1,-1]];\n```\n\nThat's basically all there is to it!\nIf you're ready for a little more Python in your JavaScript, then hop on over to the [installation section](#installation) to get started.\n\n\n## Installation\n\nThe Slice package is available on npm with the package name [slice](https://npmjs.com/package/slice).\nYou can install it using your favorite JavaScript package manager in the usual way.\n\n```bash\n# With npm: npm install slice\n# With pnpm: pnpm install slice\n# With yarn:\nyarn add slice\n```\n\n\n## API\n\nEach of these methods and classes exist as named exports in the `slice` package.\nThey can be imported using either [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)\n\n```javascript\nimport { range, slice, SliceArray, SliceString } from 'slice';\n```\n\nor [require()](https://nodejs.org/api/modules.html#modules_require).\n\n```javascript\nconst { range, slice, SliceArray, SliceString } = require('slice');\n```\n\n\n### range(stop), range(start, stop, [step])\n\nConstructs a `SliceArray` object consisting of a sequence of integers.\nThe method signature and behavior are very similar to those of Python's `range()` method, and [their documentation about the method](https://docs.python.org/3/library/stdtypes.html#typesseq-range) largely applies here.\nThe value of `range(start, stop, step)[i]` will be equal to `start + (step * i)` and the `stop` parameter determines the stopping condition depending on the sign of `step`.\n- `start` \u003cnumber\u003e The value of the first element in the range, or `0` if not specified.\n- `stop` \u003cnumber\u003e The number that, once reached, will terminate the range.\n    This value will *not* be included in the range.\n- `step` \u003cnumber\u003e The difference between adjacent numbers in the range, or `1` if not specified.\n    Negative values for `step` mean that the values in the range are sequentially decreasing.\n- returns: \u003c`SliceArray`\u003e\n\n\n### slice(stop), slice(start, stop, [step])\n\nConstructs a `Slice` object which can be passed as an index to either a `SliceArray` or `SliceString` instance to specify a series of elements.\nThere's generally no need to manually construct `Slice` objects, and the double bracket `[[start,stop,step]]` indexing syntax should be preferred.\nThe method signature and behavior are identical to those of Python's [slice()](https://docs.python.org/3/library/functions.html#slice) method.\n\n- `start` \u003cnumber\u003e The index of the first element, or the index of the first/last element for positive/negative values of `step` if not specified.\n- `stop` \u003cnumber\u003e The index that, once reached, will terminate the slice.\n    If not specified, then the slice will continue until an edge of the iterable has been reached.\n- `step` \u003cnumber\u003e The gap between adjacent indices in the slice, or `1` if not specified.\n    Negative values for `step` mean that the indices in the range are sequentially decreasing.\n- returns: \u003c`Slice`\u003e\n\n### SliceArray(arrayLength) / SliceArray(element0, element1[, ...[, elementN]])\n\nConstructs a `SliceArray` object which adds support for negative indexing and slicing to the built-in [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) object.\nThe API for `SliceArray` is identical to that of `Array`, and it can be used as a drop-in replacement.\nAny methods that would normally return an `Array` will return a `SliceArray` instead.\n\n\n### SliceString(thing)\n\nConstructs a `SliceString` object which adds support for negative indexing and slicing to the built-in [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) object.\nThe API for `SliceString` is identical to that of `String`, and it can be used as a drop-in replacement.\nAny methods that would normally return an `String` will return a `SliceString` instead.\n\n\n## Development\n\nTo get started on development, you simply need to clone the repository and install the project dependencies.\n\n```bash\n# Clone the repository.\ngit clone https://github.com/intoli/slice.git\ncd slice\n\n# Install the dependencies.\nyarn install\n\n# Build the project.\nyarn build\n\n# Run the tests.\nyarn test\n```\n\nThere is also a separate test suite which generates many thousands of slice operations on the fly in Python.\nThese generated operations are then applied in JavaScript to confirm that everything works as expected.\nThe auto-generated tests can be run with the `yarn test:generated` command.\n\n\n## Contributing\n\nContributions are welcome, but please follow these contributor guidelines outlined in [CONTRIBUTING.md](CONTRIBUTING.md).\n\n\n## License\n\nSlice is licensed under a [BSD 2-Clause License](LICENSE) and is copyright [Intoli, LLC](https://intoli.com).\n","funding_links":[],"categories":["Proxy Resources"],"sub_categories":["Modules"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintoli%2Fslice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintoli%2Fslice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintoli%2Fslice/lists"}