Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kleinfreund/reverse-iterable-map
A reverse-iterable map implementation based on the built-in Map object.
https://github.com/kleinfreund/reverse-iterable-map
iterable iterator map reverse-iterable
Last synced: 18 days ago
JSON representation
A reverse-iterable map implementation based on the built-in Map object.
- Host: GitHub
- URL: https://github.com/kleinfreund/reverse-iterable-map
- Owner: kleinfreund
- License: mit
- Created: 2017-12-06T13:28:51.000Z (almost 7 years ago)
- Default Branch: main
- Last Pushed: 2024-10-20T18:58:00.000Z (about 1 month ago)
- Last Synced: 2024-10-21T02:11:29.945Z (about 1 month ago)
- Topics: iterable, iterator, map, reverse-iterable
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/reverse-iterable-map
- Size: 957 KB
- Stars: 2
- Watchers: 3
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# reverse-iterable-map
The `ReverseIterableMap` object is a reverse-iterable map implementation based on the built-in [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object.
It implements a linked list meaning that each element in the internal data structure (a `Map` object) knows about its previous and next element; thus, allowing iteration in both directions *at the same time*. This implies added memory usage because in addition to its key and value, an element also needs to store the two references for the previous and next elements.
Links:
- [**npmjs.com**/package/reverse-iterable-map](https://www.npmjs.com/package/reverse-iterable-map)
- [on BundlePhobia](https://bundlephobia.com/result?p=reverse-iterable-map)
- [**github.com**/kleinfreund/reverse-iterable-map](https://github.com/kleinfreund/reverse-iterable-map)See also:
- `ReverseIterableArray`: [reverse-iterable-array](https://www.npmjs.com/package/reverse-iterable-array)
- `ReverseIterableSet`: [reverse-iterable-set](https://www.npmjs.com/package/reverse-iterable-set)## Table of contents
- [Installation](#installation)
- [Usage](#usage)
- [Examples](#examples)
- [Tests](#tests)
- [Documentation](#documentation)
- [Constructor](#constructor)
- [`size`](#size)
- [`[Symbol.toStringTag]`](#symboltostringtag)
- [`clear()`](#clear)
- [`delete()`](#delete)
- [`entries()`](#entries)
- [`forEach()`](#foreach)
- [`forEachReverse()`](#foreachreverse)
- [`get()`](#get)
- [`has()`](#has)
- [`iteratorFor()`](#iteratorfor)
- [`keys()`](#keys)
- [`reverseIterator()`](#reverseiterator)
- [`set()`](#set)
- [`setFirst()`](#setfirst)
- [`[Symbol.iterator]()`](#symboliterator)
- [`values()`](#values)
- [Why this was implemented](#why-this-was-implemented)
- [How to update this package](#how-to-update-this-package)## Installation
```sh
npm install reverse-iterable-map
```## Usage
```js
import ReverseIterableMap from 'reverse-iterable-map';const map = new ReverseIterableMap();
```## Examples
For some live usage examples, clone the repository and run the following:
```sh
npm install
npm run build
npm start
```Then, open [localhost:8080/examples](http://127.0.0.1:8080/examples) in a browser.
## Tests
In order to run the tests, clone the repository and run the following:
```sh
npm install
npm test
```## Documentation
**Disclaimer**: The documentation section copies a lot of content from the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) documentation on the Mozilla Developer Network.
A `ReverseIterableMap` object iterates its elements in insertion or reverse-insertion order — a [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop returns an array of `[key, value]` for each iteration.
### Constructor
#### Syntax
```
new ReverseIterableMap([iterable])
```**Parameters**:
- `iterable`: An `Array` or other [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol) object whose elements are key-value pairs.
#### Usage
- Without arguments
```js
const map = new ReverseIterableMap();
```- `Array`
```js
const map = new ReverseIterableMap([1, 2, 3].entries());
```- `Array` of `Array`s
```js
const map = new ReverseIterableMap([[0, 1], [1, 2], [2, 3]]);
```- `Map`
```js
const builtInMap = new Map([['key1', 1], ['key2', 2], ['key3', 3]]);
const map = new ReverseIterableMap(builtInMap);
```- `NodeList`
```js
const nodeList = document.querySelectorAll('a');
const map = new ReverseIterableMap(nodeList.entries());
```### `size`
The `size` accessor property returns the number of elements in a `ReverseIterableMap` object.
#### Syntax
```
map.size
```#### Usage
```js
const map = new ReverseIterableMap()
.set('one', 'I')
.set('two', 'lack')
.set('three', 'creativity');map.size
//> 3
```### `[Symbol.toStringTag]`
The `ReverseIterableMap[@@toStringTag]` property has an initial value of “ReverseIterableMap”.
### `clear()`
#### Syntax
```
map.clear();
```**Return value**:
[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).
#### Usage
```js
// Clears the underlying Map object
// Sets the first and last node references to `null`
map.clear();
```### `delete()`
#### Syntax
```
map.delete(key);
```**Parameters**:
- **key**: Required. The key of the element to remove from the `ReverseIterableMap` object.
**Return value**:
- **Boolean**: Returns `true` if an element in the `ReverseIterableMap` object existed and has been removed, or `false` if the element does not exist.
#### Usage
```js
const map = new ReverseIterableMap(['hey', 'beauty'].entries());map.delete(0);
//> true (deletes the key value pair [0, 'hey'])map.delete(1);
//> true (deletes the key value pair [1, 'beauty'])map.delete(2);
//> false (key 2 does not exist in map)
```### `entries()`
Returns an iterator containing the `[key, value]` pairs for each element in the `ReverseIterableMap` object in insertion order.
An iterator containing the same pairs in reverse-insertion order can be obtained with `entries().reverseIterator()`.
#### Syntax
```
map.entries();
```**Return value**:
A new `ReverseIterableMap` iterator object.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());const iterator = map.entries();
iterator.next().value;
//> [0, 1]iterator.next().value;
//> [1, 2]iterator.next().value;
//> [2, 4]iterator.next().value;
//> undefined
```### `forEach()`
The `forEach()` method executes a provided function once for each `[key, value]` pair in the `ReverseIterableMap` object, in insertion order.
#### Syntax
```
map.forEach(callback[, thisArg]);
```**Parameters**:
- **callbackfn**: Function to execute for each element. The `callbackfn` gets passed references to the current `value`, `key`, and a reference to the `ReverseIterableMap` object itself.
- **thisArg**: Value to use as `this` when executing `callback`.
**Return value**:
[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).
#### Usage
```js
const map = new ReverseIterableMap([
[0, 'a'],
[1, 'b'],
[2, 'c']
]);map.forEach(value => {
console.log(value);
});
//> a
//> b
//> cmap.forEach(function (value, key, mapReference) {
console.log(key, value, mapReference.size);
});
//> 0 a 3
//> 1 b 3
//> 2 c 3
```### `forEachReverse()`
The `forEachReverse()` method executes a provided function once per each `[key, value]` pair in the `ReverseIterableMap` object, in reverse-insertion order.
#### Syntax
```
map.forEachReverse(callback[, thisArg]);
```**Parameters**:
- **callback**: Function to execute for each element. The `callbackfn` gets passed references to the current `value`, `key`, and a reference to the `ReverseIterableMap` object itself.
- **thisArg**: Value to use as `this` when executing `callback`.**Return value**:
[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).
#### Usage
```js
const map = new ReverseIterableMap([
[0, 'a'],
[1, 'b'],
[2, 'c']
]);map.forEachReverse(value => {
console.log(value);
});
//> c
//> b
//> amap.forEachReverse(function (value, key, mapReference) {
console.log(key, value, mapReference.size);
});
//> 2 c 3
//> 1 b 3
//> 0 a 3
```### `get()`
#### Syntax
```
map.get(key);
```**Parameters**:
- **key**: Required. The key of the element to return from the `ReverseIterableMap` object.
**Return value**:
- Returns the element associated with the specified key or `undefined` if the key can't be found in the `ReverseIterableMap` object.
#### Usage
```js
const map = new ReverseIterableMap(['hey', 'beauty'].entries());map.get(0);
//> 'hey'map.get(1);
//> 'beauty'map.get(2);
//> undefined
```### `has()`
#### Syntax
```
map.has(key);
```**Parameters**:
- **key**: Required. The key of the element to test for presence in the `ReverseIterableMap` object.
**Return value**:
- **Boolean**: Returns `true` if an element with the specified key exists in the `ReverseIterableMap` object; otherwise `false`.
#### Usage
```js
const map = new ReverseIterableMap(['hey', 'beauty'].entries());map.has(0);
//> truemap.has(1);
//> truemap.has(2);
//> false
```### `iteratorFor()`
Returns an iterator containing the `[key, value]` pairs for each element in the `ReverseIterableMap` object in insertion order **starting with the pair specified by the `key` parameter**.
This allows starting iteration at a specific element in the map.
An iterator containing the same pairs in reverse-insertion order can be obtained with `iteratorFor().reverseIterator()`.
#### Syntax
```
map.iteratorFor(key);
```**Parameters**:
- **key**: Required. The key of the element to start iterating from.
**Return value**:
A new `ReverseIterableMap` iterator object.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());// Iterator, starting at the element with key 1.
const iterator = map.iteratorFor(1);iterator.next().value;
//> [1, 2]iterator.next().value;
//> [2, 4]iterator.next().value;
//> undefined// Reverse-iterator, starting at the element with key 1.
const reverseIterator = map.iteratorFor(1).reverseIterator();reverseIterator.next().value;
//> [1, 2]reverseIterator.next().value;
//> [0, 1]reverseIterator.next().value;
//> undefined
```### `keys()`
Returns an iterator containing the keys for each element in the `ReverseIterableMap` object in insertion order.
An iterator containing the same keys in reverse-insertion order can be obtained with `keys().reverseIterator()`.
#### Syntax
```
map.keys();
```**Return value**:
A new `ReverseIterableMap` iterator object.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());const iterator = map.keys();
iterator.next().value;
//> 2iterator.next().value;
//> 1iterator.next().value;
//> 0iterator.next().value;
//> undefined
```### `reverseIterator()`
In theory, following the semantics of `[Symbol.iterator]()`, this should be `[Symbol.reverseIterator]()`. However, as a developer, I cannot define a well-known symbol myself and make use of it. In the future, the a proposal like [The ReverseIterable Interface, by Lee Byron](https://github.com/leebyron/ecmascript-reverse-iterable) might make it’s way into the specification. For the time being, the `reverseIterator()` function serves the same purpose.
#### Syntax
```
map.reverseIterator();
```**Return value**:
The map **reverse-iterator** function, which is the `entries().reverseIterator()` function by default.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());const reverseIterator = map.reverseIterator();
reverseIterator.next().value;
//> [2, 4]reverseIterator.next().value;
//> [1, 2]reverseIterator.next().value;
//> [0, 1]reverseIterator.next().value;
//> undefined
```### `set()`
#### Syntax
```
map.set(key, value);
```**Parameters**:
- **key**: Required. The key of the element to add to the `ReverseIterableMap` object.
- **value**: Required. The value of the element to add to the `ReverseIterableMap` object.**Return value**:
- The `ReverseIterableMap` object.
#### Usage
```js
const map = new ReverseIterableMap();map.set('you', 'beauty');
//> mapmap.set('the-magic-key', 'hey');
//> map
```The `set()` method returns a reference to the map object. This makes the set operation chainable.
```js
const map = new ReverseIterableMap()
.set('key', '… is spelled like tea')
.set('hey', '… somehow ney');
```### `setFirst()`
The `setFirst()` method functions like `set()` but uses reverse-insertion order.
#### Syntax
```
map.set(key, value);
```**Parameters**:
- **key**: Required. The key of the element to add to the `ReverseIterableMap` object.
- **value**: Required. The value of the element to add to the `ReverseIterableMap` object.**Return value**:
- The `ReverseIterableMap` object.
#### Usage
```js
const map = new ReverseIterableMap()
.setFirst('key1', 'was inserted first')
.setFirst('key2', 'was inserted last');map.values().next().value;
//> 'was inserted last'map.values().reverseIterator().next().value;
//> 'was inserted first'
```### `[Symbol.iterator]()`
Returns the map iterator function. By default, this is the `entries()` function.
#### Syntax
```
map[Symbol.iterator]();
```**Return value**:
The map **iterator** function, which is the `entries()` function by default.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());const iterator = map[Symbol.iterator]();
iterator.next().value;
//> [0, 1]iterator.next().value;
//> [1, 2]iterator.next().value;
//> [2, 4]iterator.next().value;
//> undefined
```### `values()`
Returns an iterator containing the values for each element in the `ReverseIterableMap` object in insertion order.
An iterator containing the same values in reverse-insertion order can be obtained with `values().reverseIterator()`.
#### Syntax
```
map.values();
```**Return value**:
A new `ReverseIterableMap` iterator object.
#### Usage
```js
const map = new ReverseIterableMap([1, 2, 4].entries());const iterator = map.values();
iterator.next().value;
//> 1iterator.next().value;
//> 2iterator.next().value;
//> 4iterator.next().value;
//> undefined
```## Why this was implemented
Part of the additions to ECMAScript 2015 are the [iteration protocols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols): [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol) and [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol). The former allows arbitrary objects to become iterable. Following the rules of the protocol gives one iteration capabilities via the following techniques:
- [`for...of` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
- [`Array.from()` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
- [Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)**However**, only the iteration in one direction is considered by the [specification](https://www.ecma-international.org/ecma-262/6.0/#sec-iteration) at the time. This means that we only get forward-iteration by default. There is a draft for a proposal to add a `ReverseIterable` interface to the specification: [“The ReverseIterable Interface” by Lee Byron](https://github.com/leebyron/ecmascript-reverse-iterable).
Now, with the iteration protocols, we could redefine the iteration behavior for our purpose and make an object backwards-iterable. At the same time, this means losing the ability to iterate forwards.
**If you need both a forwards- and backwards-iterable object, this implementation might be for you.**
**But why a map?**
That’s what I needed. To be precise, I needed to access an iterator _at a specific location_ in my data structure and be able to _iterate in both directions_.
I tried to stick to the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) interface as close as possible.
Implementing a reverse-iterable array, for example, can be accomplished by using the same techniques of this implementation.
## How to update this package
… because I keep forgetting that.
Let’s assume a *minor* update was made. First of all, the working directory needs to be cleaned up; all changes need to be committed. It’s important to run the `build` script to make sure new CommonJS and ES modules are compiled from the TypeScript source module.
```sh
npm run build
git commit -am "Implemented extremely nice feature"
```Next, make sure you have a valid NPM authentication token set up:
```sh
npm whoami
```If not, do that with `npm login` and continue. We now create a new commit with the next *minor* version tag and update the package.json. Actually, the following command will do that:
```sh
npm version minor # See `npm version --help` for more options
```This creates a new git tag that we need to publish as well. With that, we can now publish the new version.
```sh
git push && git push --tags
npm publish
```That’s it.