{"id":17444674,"url":"https://github.com/kleinfreund/reverse-iterable-map","last_synced_at":"2026-02-16T19:09:12.913Z","repository":{"id":32839885,"uuid":"113319980","full_name":"kleinfreund/reverse-iterable-map","owner":"kleinfreund","description":"A reverse-iterable map implementation based on the built-in Map object.","archived":false,"fork":false,"pushed_at":"2024-10-20T18:58:00.000Z","size":980,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-21T02:11:29.945Z","etag":null,"topics":["iterable","iterator","map","reverse-iterable"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/reverse-iterable-map","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kleinfreund.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-06T13:28:51.000Z","updated_at":"2024-10-20T18:58:03.000Z","dependencies_parsed_at":"2024-10-25T18:42:43.360Z","dependency_job_id":"5265f37a-2e0e-4382-81bc-ff111d539300","html_url":"https://github.com/kleinfreund/reverse-iterable-map","commit_stats":{"total_commits":172,"total_committers":3,"mean_commits":"57.333333333333336","dds":0.4651162790697675,"last_synced_commit":"fa05e473906469b3997773f80342e0916e8d0947"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Freverse-iterable-map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Freverse-iterable-map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Freverse-iterable-map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kleinfreund%2Freverse-iterable-map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kleinfreund","download_url":"https://codeload.github.com/kleinfreund/reverse-iterable-map/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233710888,"owners_count":18717950,"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":["iterable","iterator","map","reverse-iterable"],"created_at":"2024-10-17T17:53:25.484Z","updated_at":"2025-09-21T04:31:13.690Z","avatar_url":"https://github.com/kleinfreund.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# reverse-iterable-map\n\nThe `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.\n\nIt 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.\n\nLinks:\n\n- [**npmjs.com**/package/reverse-iterable-map](https://www.npmjs.com/package/reverse-iterable-map)\n\t- [on BundlePhobia](https://bundlephobia.com/result?p=reverse-iterable-map)\n- [**github.com**/kleinfreund/reverse-iterable-map](https://github.com/kleinfreund/reverse-iterable-map)\n\nSee also:\n\n- `ReverseIterableArray`: [reverse-iterable-array](https://www.npmjs.com/package/reverse-iterable-array)\n- `ReverseIterableSet`: [reverse-iterable-set](https://www.npmjs.com/package/reverse-iterable-set)\n\n## Table of contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Examples](#examples)\n- [Tests](#tests)\n- [Documentation](#documentation)\n\t- [Constructor](#constructor)\n\t- [`size`](#size)\n\t- [`[Symbol.toStringTag]`](#symboltostringtag)\n\t- [`clear()`](#clear)\n\t- [`delete()`](#delete)\n\t- [`entries()`](#entries)\n\t- [`forEach()`](#foreach)\n\t- [`forEachReverse()`](#foreachreverse)\n\t- [`get()`](#get)\n\t- [`has()`](#has)\n\t- [`iteratorFor()`](#iteratorfor)\n\t- [`keys()`](#keys)\n\t- [`reverseIterator()`](#reverseiterator)\n\t- [`set()`](#set)\n\t- [`setFirst()`](#setfirst)\n\t- [`[Symbol.iterator]()`](#symboliterator)\n\t- [`values()`](#values)\n- [Why this was implemented](#why-this-was-implemented)\n- [How to update this package](#how-to-update-this-package)\n\n## Installation\n\n```sh\nnpm install reverse-iterable-map\n```\n\n## Usage\n\n```js\nimport ReverseIterableMap from 'reverse-iterable-map';\n\nconst map = new ReverseIterableMap();\n```\n\n## Examples\n\nFor some live usage examples, clone the repository and run the following:\n\n```sh\nnpm install\nnpm run build\nnpm start\n```\n\nThen, open [localhost:8080/examples](http://127.0.0.1:8080/examples) in a browser.\n\n## Tests\n\nIn order to run the tests, clone the repository and run the following:\n\n```sh\nnpm install\nnpm test\n```\n\n## Documentation\n\n**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.\n\nA `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.\n\n### Constructor\n\n#### Syntax\n\n```\nnew ReverseIterableMap([iterable])\n```\n\n**Parameters**:\n\n- `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.\n\n#### Usage\n\n- Without arguments\n\n\t```js\n\tconst map = new ReverseIterableMap();\n\t```\n\n- `Array`\n\n\t```js\n\tconst map = new ReverseIterableMap([1, 2, 3].entries());\n\t```\n\n- `Array` of `Array`s\n\n\t```js\n\tconst map = new ReverseIterableMap([[0, 1], [1, 2], [2, 3]]);\n\t```\n\n- `Map`\n\n\t```js\n\tconst builtInMap = new Map([['key1', 1], ['key2', 2], ['key3', 3]]);\n\tconst map = new ReverseIterableMap(builtInMap);\n\t```\n\n- `NodeList`\n\n\t```js\n\tconst nodeList = document.querySelectorAll('a');\n\tconst map = new ReverseIterableMap(nodeList.entries());\n\t```\n\n\t### `size`\n\nThe `size` accessor property returns the number of elements in a `ReverseIterableMap` object.\n\n#### Syntax\n\n```\nmap.size\n```\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap()\n\t.set('one', 'I')\n\t.set('two', 'lack')\n\t.set('three', 'creativity');\n\nmap.size\n//\u003e 3\n```\n\n### `[Symbol.toStringTag]`\n\nThe `ReverseIterableMap[@@toStringTag]` property has an initial value of “ReverseIterableMap”.\n\n\n### `clear()`\n\n#### Syntax\n\n```\nmap.clear();\n```\n\n**Return value**:\n\n[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).\n\n#### Usage\n\n```js\n// Clears the underlying Map object\n// Sets the first and last node references to `null`\nmap.clear();\n```\n\n### `delete()`\n\n#### Syntax\n\n```\nmap.delete(key);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to remove from the `ReverseIterableMap` object.\n\n**Return value**:\n\n- **Boolean**: Returns `true` if an element in the `ReverseIterableMap` object existed and has been removed, or `false` if the element does not exist.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap(['hey', 'beauty'].entries());\n\nmap.delete(0);\n//\u003e true (deletes the key value pair [0, 'hey'])\n\nmap.delete(1);\n//\u003e true (deletes the key value pair [1, 'beauty'])\n\nmap.delete(2);\n//\u003e false (key 2 does not exist in map)\n```\n\n### `entries()`\n\nReturns an iterator containing the `[key, value]` pairs for each element in the `ReverseIterableMap` object in insertion order.\n\nAn iterator containing the same pairs in reverse-insertion order can be obtained with `entries().reverseIterator()`.\n\n#### Syntax\n\n```\nmap.entries();\n```\n\n**Return value**:\n\nA new `ReverseIterableMap` iterator object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\nconst iterator = map.entries();\n\niterator.next().value;\n//\u003e [0, 1]\n\niterator.next().value;\n//\u003e [1, 2]\n\niterator.next().value;\n//\u003e [2, 4]\n\niterator.next().value;\n//\u003e undefined\n```\n\n### `forEach()`\n\nThe `forEach()` method executes a provided function once for each `[key, value]` pair in the `ReverseIterableMap` object, in insertion order.\n\n#### Syntax\n\n```\nmap.forEach(callback[, thisArg]);\n```\n\n**Parameters**:\n\n- **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.\n\n- **thisArg**: Value to use as `this` when executing `callback`.\n\n**Return value**:\n\n[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([\n\t[0, 'a'],\n\t[1, 'b'],\n\t[2, 'c']\n]);\n\nmap.forEach(value =\u003e {\n\tconsole.log(value);\n});\n//\u003e a\n//\u003e b\n//\u003e c\n\nmap.forEach(function (value, key, mapReference) {\n\tconsole.log(key, value, mapReference.size);\n});\n//\u003e 0 a 3\n//\u003e 1 b 3\n//\u003e 2 c 3\n```\n\n### `forEachReverse()`\n\nThe `forEachReverse()` method executes a provided function once per each `[key, value]` pair in the `ReverseIterableMap` object, in reverse-insertion order.\n\n#### Syntax\n\n```\nmap.forEachReverse(callback[, thisArg]);\n```\n\n**Parameters**:\n\n- **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.\n- **thisArg**: Value to use as `this` when executing `callback`.\n\n**Return value**:\n\n[`undefined`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined).\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([\n\t[0, 'a'],\n\t[1, 'b'],\n\t[2, 'c']\n]);\n\nmap.forEachReverse(value =\u003e {\n\tconsole.log(value);\n});\n//\u003e c\n//\u003e b\n//\u003e a\n\nmap.forEachReverse(function (value, key, mapReference) {\n\tconsole.log(key, value, mapReference.size);\n});\n//\u003e 2 c 3\n//\u003e 1 b 3\n//\u003e 0 a 3\n```\n\n### `get()`\n\n#### Syntax\n\n```\nmap.get(key);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to return from the `ReverseIterableMap` object.\n\n**Return value**:\n\n- Returns the element associated with the specified key or `undefined` if the key can't be found in the `ReverseIterableMap` object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap(['hey', 'beauty'].entries());\n\nmap.get(0);\n//\u003e 'hey'\n\nmap.get(1);\n//\u003e 'beauty'\n\nmap.get(2);\n//\u003e undefined\n```\n\n### `has()`\n\n#### Syntax\n\n```\nmap.has(key);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to test for presence in the `ReverseIterableMap` object.\n\n**Return value**:\n\n- **Boolean**: Returns `true` if an element with the specified key exists in the `ReverseIterableMap` object; otherwise `false`.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap(['hey', 'beauty'].entries());\n\nmap.has(0);\n//\u003e true\n\nmap.has(1);\n//\u003e true\n\nmap.has(2);\n//\u003e false\n```\n\n### `iteratorFor()`\n\nReturns 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**.\n\nThis allows starting iteration at a specific element in the map.\n\nAn iterator containing the same pairs in reverse-insertion order can be obtained with `iteratorFor().reverseIterator()`.\n\n#### Syntax\n\n```\nmap.iteratorFor(key);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to start iterating from.\n\n**Return value**:\n\nA new `ReverseIterableMap` iterator object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\n// Iterator, starting at the element with key 1.\nconst iterator = map.iteratorFor(1);\n\niterator.next().value;\n//\u003e [1, 2]\n\niterator.next().value;\n//\u003e [2, 4]\n\niterator.next().value;\n//\u003e undefined\n\n// Reverse-iterator, starting at the element with key 1.\nconst reverseIterator = map.iteratorFor(1).reverseIterator();\n\nreverseIterator.next().value;\n//\u003e [1, 2]\n\nreverseIterator.next().value;\n//\u003e [0, 1]\n\nreverseIterator.next().value;\n//\u003e undefined\n```\n\n### `keys()`\n\nReturns an iterator containing the keys for each element in the `ReverseIterableMap` object in insertion order.\n\nAn iterator containing the same keys in reverse-insertion order can be obtained with `keys().reverseIterator()`.\n\n#### Syntax\n\n```\nmap.keys();\n```\n\n**Return value**:\n\nA new `ReverseIterableMap` iterator object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\nconst iterator = map.keys();\n\niterator.next().value;\n//\u003e 2\n\niterator.next().value;\n//\u003e 1\n\niterator.next().value;\n//\u003e 0\n\niterator.next().value;\n//\u003e undefined\n```\n\n### `reverseIterator()`\n\nIn 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.\n\n#### Syntax\n\n```\nmap.reverseIterator();\n```\n\n**Return value**:\n\nThe map **reverse-iterator** function, which is the `entries().reverseIterator()` function by default.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\nconst reverseIterator = map.reverseIterator();\n\nreverseIterator.next().value;\n//\u003e [2, 4]\n\nreverseIterator.next().value;\n//\u003e [1, 2]\n\nreverseIterator.next().value;\n//\u003e [0, 1]\n\nreverseIterator.next().value;\n//\u003e undefined\n```\n\n### `set()`\n\n#### Syntax\n\n```\nmap.set(key, value);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to add to the `ReverseIterableMap` object.\n- **value**: Required. The value of the element to add to the `ReverseIterableMap` object.\n\n**Return value**:\n\n- The `ReverseIterableMap` object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap();\n\nmap.set('you', 'beauty');\n//\u003e map\n\nmap.set('the-magic-key', 'hey');\n//\u003e map\n```\n\nThe `set()` method returns a reference to the map object. This makes the set operation chainable.\n\n```js\nconst map = new ReverseIterableMap()\n\t.set('key', '… is spelled like tea')\n\t.set('hey', '… somehow ney');\n```\n\n### `setFirst()`\n\nThe `setFirst()` method functions like `set()` but uses reverse-insertion order.\n\n#### Syntax\n\n```\nmap.set(key, value);\n```\n\n**Parameters**:\n\n- **key**: Required. The key of the element to add to the `ReverseIterableMap` object.\n- **value**: Required. The value of the element to add to the `ReverseIterableMap` object.\n\n**Return value**:\n\n- The `ReverseIterableMap` object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap()\n\t.setFirst('key1', 'was inserted first')\n\t.setFirst('key2', 'was inserted last');\n\nmap.values().next().value;\n//\u003e 'was inserted last'\n\nmap.values().reverseIterator().next().value;\n//\u003e 'was inserted first'\n```\n\n### `[Symbol.iterator]()`\n\nReturns the map iterator function. By default, this is the `entries()` function.\n\n#### Syntax\n\n```\nmap[Symbol.iterator]();\n```\n\n**Return value**:\n\nThe map **iterator** function, which is the `entries()` function by default.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\nconst iterator = map[Symbol.iterator]();\n\niterator.next().value;\n//\u003e [0, 1]\n\niterator.next().value;\n//\u003e [1, 2]\n\niterator.next().value;\n//\u003e [2, 4]\n\niterator.next().value;\n//\u003e undefined\n```\n\n### `values()`\n\nReturns an iterator containing the values for each element in the `ReverseIterableMap` object in insertion order.\n\nAn iterator containing the same values in reverse-insertion order can be obtained with `values().reverseIterator()`.\n\n#### Syntax\n\n```\nmap.values();\n```\n\n**Return value**:\n\nA new `ReverseIterableMap` iterator object.\n\n#### Usage\n\n```js\nconst map = new ReverseIterableMap([1, 2, 4].entries());\n\nconst iterator = map.values();\n\niterator.next().value;\n//\u003e 1\n\niterator.next().value;\n//\u003e 2\n\niterator.next().value;\n//\u003e 4\n\niterator.next().value;\n//\u003e undefined\n```\n\n## Why this was implemented\n\nPart 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:\n\n- [`for...of` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)\n- [`Array.from()` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from)\n- [Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator)\n\n**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).\n\nNow, 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.\n\n**If you need both a forwards- and backwards-iterable object, this implementation might be for you.**\n\n**But why a map?**\n\nThat’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_.\n\nI tried to stick to the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) interface as close as possible.\n\nImplementing a reverse-iterable array, for example, can be accomplished by using the same techniques of this implementation.\n\n## How to update this package\n\n… because I keep forgetting that.\n\nLet’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.\n\n```sh\nnpm run build\ngit commit -am \"Implemented extremely nice feature\"\n```\n\nNext, make sure you have a valid NPM authentication token set up:\n\n```sh\nnpm whoami\n```\n\nIf 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:\n\n```sh\nnpm version minor # See `npm version --help` for more options\n```\n\nThis creates a new git tag that we need to publish as well. With that, we can now publish the new version.\n\n```sh\ngit push \u0026\u0026 git push --tags\nnpm publish\n```\n\nThat’s it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkleinfreund%2Freverse-iterable-map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkleinfreund%2Freverse-iterable-map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkleinfreund%2Freverse-iterable-map/lists"}