A JavaScript implementation of Python's extended slice syntax.
npm install slice
alt="Build Status">
alt="License">
alt="NPM Version">
alt="Tweet">
alt="Share on Facebook">
alt="Share on Reddit">
alt="Share on Hacker News">
###### For Python Programmers | Installation | API | Contributing
> Slice is a JavaScript implementation of Python's awesome negative indexing and extended slice syntax for arrays and strings.
> It uses ES6 proxies to allow for an intuitive double-bracket indexing syntax which closely replicates how slices are constructed in Python.
> Oh, and it comes with an implementation of Python's range method too!
If 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 if you want).
If not—well, you're in for a treat!
Slice adds SliceArray and SliceString classes which extend the corresponding builtin types to provide a unified and concise syntax for indexing and slicing in JavaScript.
For starters, negative indices can be used to count backwards from the end of an array or string.
``javascript
const array = SliceArray(1, 2, 3, 4);
// Outputs: 4
array[-1]
const string = SliceString('Hello World!');
// Outputs: 'd'
string[-2]
`
That'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.array[[start,stop]]
Slice also introduces a double bracket indexing syntax which allows you to specify subranges of iterables by writing .
`javascript`
const array = SliceArray(1, 2, 3, 4);
// Outputs: [2, 3]
array[[1,-1]]
This is functionally identical to the builtin Array.slice() method, but it also works for strings and it supports assignment using the same interface.
`javascript`
const array = SliceArray(1, 2, 3, 4);
array[[1,-1]] = ['two', 'three', 'three and a half'];
// Outputs: [1, 'two', 'three', 'three and half', 4]
array
It'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.
`javascript
const array = SliceArray(1, 2, 3, 4);
// Outputs: [2, 3, 4]
array[[1,]]
// Outputs: [1, 2, 3]
array[[,-1]]
const string = SliceString('Hello World!');
// Outputs: 'World!'
string[[6,]]
`
You can also add a third step parameter to your slices using the array[[start,stop,step]] syntax.step
That's when things get really interesting.
The parameter allows you to easily extract every Nth element from an iterable while optionally specifying a subrange at the same time.
`javascript
const array = SliceArray(1, 2, 3, 4);
// Outputs: [1, 3]
array[[,,2]]
// Outputs: [2, 4]
array[[1,,2]]
`
And, of course, extended slices also support assignment!
`javascript
const array = SliceArray(1, 2, 3, 4);
array[[,,2]] = ['odd', 'odd'];
// Outputs: ['odd', 2, 'odd', 4]
array
[array[[,,2]], array[[1,,2]]] = [array[[1,,2]], array[[,,2]]];
// Outputs: [2, 'odd', 4, 'odd']
array
`
You can even use negative values for the step parameter to iterate backwards through an array or string.
`javascript
const array = SliceArray(1, 2, 3, 4);
// Outputs: [4, 3, 2, 1]
array[[,,-1]]
// Outputs: [4, 2]
array[[,,-2]]
`
Let's put this together into one last example that's a little more fun.
We'll use Slice's extended slice syntax and it's range() function to solve Fizz Buzz without any explicit loops or recursion.
`javascript
import { range, SliceArray } from 'slice';
// Populate a list from 1 through 100.
const outputs = range(1, 100 + 1);
// Replace every 3rd element with 'Fizz'.
outputs[[3 - 1,,3]] =
Array(Math.floor(100 / 3))
.fill('Fizz');
// Replace every 5th element with 'Buzz'.
outputs[[5 - 1,,5]] =
Array(Math.floor(100 / 5))
.fill('Buzz');
// Replace every (3 * 5)th element with 'Fizz Buzz'.
outputs[[3 5 - 1,,3 5]] =
Array(Math.floor(100 / (3 * 5)))
.fill('Fizz Buzz');
// Tada!
console.log(outputs);
`
If you're ready to give it a try, then head over to the installation section or take a look at the API documentation.
You also might find the For People Who Know Python Already section interesting, even if you've never used Python before.
It provides some context for why this library exists and works the way that it does.
If you know Python already, then you'll be right at home with Slice.
The methods and syntax that it introduces are designed to very closely mirror those from Python.
Python includes two built-in functions that Slice provides analogues of: range() and slice().
The method signatures of these methods are identical to those used in Python, and the behavior and usage of them is very similar.
One 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.range()
This choice was made because Python has built-in support for its slice syntax, but JavaScript requires subclassing String and Array in order to add support for a similar syntax.
The method returns a SliceArray so that the return value immediately supports slicing for convenience.
For example, you could run the following without needing to explicitly construct a SliceArray.
`javascript
import { range, slice } from 'slice';
// Outputs: [10, 11, 12, 13, 14]
range(100)[slice(10, 15)]
`
Aside from the imports, the actual usage of range() and slice() here is also valid Python and would produce the same result.slice()
Even if you use Python quite a bit, however, there's a good chance that you might not that familiar with the explicit usage of like this.slice
That's because it's way more common to use Python's slice syntax rather than manually instantiating the class.
`python`These are both equivalent in Python.
range(100)[slice(10, 15)]
range(100)[10:15]
It'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.
All you need to do is to use double brackets for the indexing and to replace the colons with commas.
The slicing will work exactly as you would expect in Python after that.
It supports negative indexing, empty parameters, extended slices, negative steps, assignment to slices, and the whole shebang.
In fact, part of the test suite actually runs a Python script to perform thousands of slicing operations to verify that the JavaScript results are identical!
Here are a few examples of how the syntax compares between Python and Slice in JavaScript.
| Input | Python Code | JavaScript Code | Output |
|-------------------|------------------|--------------------|-------------------|
| [0, 1, 2, 3, 4] | array[-2] | array[[-2]] | 3 |[0, 1, 2, 3, 4]
| | array[:2] | array[[,2]] | [0, 1] |[0, 1, 2, 3, 4]
| | array[1::2] | array[[1,,2]] | [1, 3] |[0, 1, 2, 3, 4]
| | array[::-1] | array[[,,-1]] | [4, 3, 2, 1, 0] |'hello world'
| | string[::-1] | string[[,,-1]] | 'dlrow olleh' |'hello world'
| | string[1:-1] | string[[1,-1]] | 'ello worl' |'hello world'
| | string[1:-1:2] | string[[1,-1,2]] | 'el o l' |'hello world'
| | string[:-5] | string[[,-5]] | 'world' |
Once you get used to how the Python syntax maps to the double bracket syntax, it becomes quite easy to switch seamlessly between the two.
We'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.Array
These classes have identical interfaces into JavaScript's built-in Array and String objects.
They can be constructed in exactly the same ways and are essentially drop-in replacements for and String.
`javascript
import { range, SliceArray, SliceString } from 'slice';
// All of the following are equivalent.
range(5);
SliceArray(0, 1, 2, 3, 4);
new SliceArray(0, 1, 2, 3, 4);
SliceArray.from([0, 1, 2, 3, 4]);
// The following are also equivalent.
SliceString('hello world');
new SliceString('hello world');
`
They also support all of the same methods once constructed, but will return slice-able arrays and strings whenever possible.
For example, you can do things like this without needing to worry about converting the method outputs to slice-able objects.
`javascript
const helloWorld = SliceString('hello world');
// Outputs: 'DLROW OLLEH'
helloWorld.toUpperCase()[[,,-1]];
// Outputs: [1, 4, 6]
range(5).map(i => i * 2)[[1,-1]];
`
That's basically all there is to it!
If you're ready for a little more Python in your JavaScript, then hop on over to the installation section to get started.
The Slice package is available on npm with the package name slice.
You can install it using your favorite JavaScript package manager in the usual way.
`bash`With npm: npm install slice
With pnpm: pnpm install slice
With yarn:
yarn add slice
Each of these methods and classes exist as named exports in the slice package.
They can be imported using either import
`javascript`
import { range, slice, SliceArray, SliceString } from 'slice';
or require().
`javascript`
const { range, slice, SliceArray, SliceString } = require('slice');
Constructs a SliceArray object consisting of a sequence of integers.range()
The method signature and behavior are very similar to those of Python's method, and their documentation about the method largely applies here.range(start, stop, step)[i]
The value of will be equal to start + (step * i) and the stop parameter determines the stopping condition depending on the sign of step.start
- 0 if not specified.stop
- step
This value will not be included in the range.
- 1 if not specified.step
Negative values for mean that the values in the range are sequentially decreasing.SliceArray
- returns: <>
Constructs a Slice object which can be passed as an index to either a SliceArray or SliceString instance to specify a series of elements.Slice
There's generally no need to manually construct objects, and the double bracket [[start,stop,step]] indexing syntax should be preferred.
The method signature and behavior are identical to those of Python's slice() method.
- start step if not specified.stop
- step
If not specified, then the slice will continue until an edge of the iterable has been reached.
- 1 if not specified.step
Negative values for mean that the indices in the range are sequentially decreasing.Slice
- returns: <>
Constructs a SliceArray object which adds support for negative indexing and slicing to the built-in Array object.SliceArray
The API for is identical to that of Array, and it can be used as a drop-in replacement.Array
Any methods that would normally return an will return a SliceArray instead.
Constructs a SliceString object which adds support for negative indexing and slicing to the built-in String object.SliceString
The API for is identical to that of String, and it can be used as a drop-in replacement.String
Any methods that would normally return an will return a SliceString instead.
To get started on development, you simply need to clone the repository and install the project dependencies.
`bashClone the repository.
git clone https://github.com/intoli/slice.git
cd slice
There is also a separate test suite which generates many thousands of slice operations on the fly in Python.
These generated operations are then applied in JavaScript to confirm that everything works as expected.
The auto-generated tests can be run with the
yarn test:generated` command.Contributions are welcome, but please follow these contributor guidelines outlined in CONTRIBUTING.md.
Slice is licensed under a BSD 2-Clause License and is copyright Intoli, LLC.