如何在 javascript 中以相反的顺序迭代 Set 或 Map?
How to iterate over a Set or Map in reverse order in javascript?
我正在寻找一种以相反顺序迭代 Set or Map 的方法。
按常规顺序考虑这个简单示例:
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
}
从Set.prototype.values() or Set.prototype.entries()给出的迭代器也是从头到尾。
以相反顺序迭代 Set(或 Map)的解决方案是什么?
您可以创建一个数组,将其反转并生成一组新数组。
var mySet = new Set([1, 2, 3, 4, 5]),
reverseSet = new Set([...mySet].reverse());
console.log([...reverseSet]);
var mySet = new Set([1,2,3,4,5]);
var reverse = [...mySet].reverse();
for(let myNum of reverse){
console.log(myNum);
}
无法在 Maps 或 Set 上获得反向迭代器,正如我在尝试 时发现的那样。所以真正唯一的方法是使用中间数组并将其反转,如下所示:
var mySet = new Set([1,2,3,4,5]);
for (let myNum of Array.from(mySet).reverse()) {
console.log(myNum);
}
或者您可以使用这个替代的双向链接集实现:
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);
}
您还可以考虑将自定义迭代器添加到您的 Set 或 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);
}
可以在 MDN 上找到其他信息:
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/@@iterator
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
您也可以使用 reduceRight()
作为循环并忽略返回的回调值。
- 根据您的循环任务,
reduceRight()
最多可以比 reverse()
快 20%..
- 对于更大的数据集(>1000 个项目)
reverse()
+ for(){}
循环似乎更快..
let mySet = new Set([1, 2, 3, 4, 5]);
let myMap = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
[4, 'four'],
[5, 'five']
]);
console.log('⎯⎯⎯⎯⎯ Set ⎯⎯⎯⎯⎯');
Array.from(mySet).reduceRight((_, v, i) => console.log(`i = ${i}; v = ${v}`), null);
console.log('⎯⎯⎯⎯⎯ Map ⎯⎯⎯⎯⎯');
Array.from(myMap).reduceRight((_, [k, v]) => console.log(`k = ${k}; v = ${v}`), null);
我正在寻找一种以相反顺序迭代 Set or Map 的方法。
按常规顺序考虑这个简单示例:
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
}
从Set.prototype.values() or Set.prototype.entries()给出的迭代器也是从头到尾。
以相反顺序迭代 Set(或 Map)的解决方案是什么?
您可以创建一个数组,将其反转并生成一组新数组。
var mySet = new Set([1, 2, 3, 4, 5]),
reverseSet = new Set([...mySet].reverse());
console.log([...reverseSet]);
var mySet = new Set([1,2,3,4,5]);
var reverse = [...mySet].reverse();
for(let myNum of reverse){
console.log(myNum);
}
无法在 Maps 或 Set 上获得反向迭代器,正如我在尝试
var mySet = new Set([1,2,3,4,5]);
for (let myNum of Array.from(mySet).reverse()) {
console.log(myNum);
}
或者您可以使用这个替代的双向链接集实现:
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);
}
您还可以考虑将自定义迭代器添加到您的 Set 或 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);
}
可以在 MDN 上找到其他信息:
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/@@iterator
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator
- https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator
您也可以使用 reduceRight()
作为循环并忽略返回的回调值。
- 根据您的循环任务,
reduceRight()
最多可以比reverse()
快 20%.. - 对于更大的数据集(>1000 个项目)
reverse()
+for(){}
循环似乎更快..
let mySet = new Set([1, 2, 3, 4, 5]);
let myMap = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
[4, 'four'],
[5, 'five']
]);
console.log('⎯⎯⎯⎯⎯ Set ⎯⎯⎯⎯⎯');
Array.from(mySet).reduceRight((_, v, i) => console.log(`i = ${i}; v = ${v}`), null);
console.log('⎯⎯⎯⎯⎯ Map ⎯⎯⎯⎯⎯');
Array.from(myMap).reduceRight((_, [k, v]) => console.log(`k = ${k}; v = ${v}`), null);