I'm looking for a a way to iterate over a Set or Map in reverse order.
Consider this simple example in regular order:
var mySet = new Set([1,2,3,4,5]);
for(let myNum of mySet) {
console.log(myNum); // output: 1, 2, 3, 4, 5 in sepearte lines
}
The iterator given from Set.prototype.values() or Set.prototype.entries() are also from start to beginning.
What would be the solution to iterate the Set (or Map) in the reverse order?
There is no way to obtain a reversed iterator on Maps or Sets, as I have found out while trying to get the last item added to a Set. So the only way really is to use an intermediate Array and reverse it, like this:
var mySet = new Set([1,2,3,4,5]);
for (let myNum of Array.from(mySet).reverse()) {
console.log(myNum);
}
Or you can use this alternate doubly linked Set implementation:
class LinkedSetLink {
constructor(value) {
this.value = value;
this.prev = this;
this.next = this;
}
insertBefore(item) {
const prev = item.prev = this.prev;
const next = item.next = this;
next.prev = item;
prev.next = item;
}
remove() {
const prev = this.prev;
const next = this.next;
next.prev = prev;
prev.next = next;
}
}
class LinkedSet {
constructor(iterable) {
this._map = new Map();
this._pivot = new LinkedSetLink(/* undefined */);
if (iterable) {
this._addAll(iterable);
}
}
_addAll(iterable) {
for (const item of iterable) {
this.add(item);
}
}
has(item) {
return this._map.has(item);
}
add(item) {
if (!this._map.has(item)) {
const link = new LinkedSetLink(item);
this._pivot.insertBefore(link);
this._map.set(item, link);
}
}
delete(item) {
const link = this._map.get(item);
if (link) {
this._map.delete(item);
link.remove();
}
}
clear() {
this._map.clear();
this._pivot.next = this._pivot.prev = this._pivot;
}
get size() {
return this._map.size;
}
values() {
return this._map.keys();
}
keys() {
return this.values();
}
[Symbol.iterator]() {
return this.values();
}
*entries() {
for (const key of this.values()) {
yield [key, key];
}
}
*reversedItems() {
let link = this._pivot.prev;
while (link !== this._pivot) {
yield link.value;
link = link.prev;
}
}
first() {
return this._pivot.next.value;
}
last() {
return this._pivot.prev.value;
}
}
const myset = new LinkedSet([1,2,3,4,5]);
for (let item of myset.reversedItems()) {
console.log(item);
}
You can also consider adding a custom iterator to your Set or Map:
const mySet = new Set([1, 2, 3, 4, 5]);
const myMap = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
[4, 'four'],
[5, 'five']
]);
const customIterator = function () {
// get the values from the Set or Map to iterate over
// you could also use .entries() instead of .values()
// to get the key/value pairs in case of the Map
const values = Array.from(this.values());
// start at the end of the array
let index = values.length;
// the custom iterator function returns an object with a next() function
// this will be called repeatedly by for...of
return {
next: function () {
// the next() function returns an object with a done property
// to indicate when iteration is completed, and a value property
// holding the current value
return {
done: index === 0,
// `--` in front, so it decreases 'in place'
value: values[--index]
};
}
}
};
// add the customIterator to the [Symbol.iterator] property
mySet[Symbol.iterator] = customIterator;
myMap[Symbol.iterator] = customIterator;
// using for...of on the Set
for(const item of mySet) {
console.log('set:', item);
// you can also break on some condition e.g.
// if(item === 3){ break; }
}
// using for...of on the Map
for(const item of myMap) {
console.log('map:', item);
}
Additional info can be found on MDN:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With