18

我正在寻找一种以相反顺序迭代SetMap的方法。

按常规顺序考虑这个简单的例子:

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()Set.prototype.entries()给出的迭代器也是从头到尾的。

以相反顺序迭代 Set(或 Map)的解决方案是什么?

4

5 回答 5

21

正如我在尝试将最后一项添加到 Set时发现的那样,无法在 Maps 或 Sets 上获得反向迭代器。所以真正的唯一方法是使用中间数组并将其反转,如下所示:

var mySet = new Set([1,2,3,4,5]);
for (let myNum of Array.from(mySet).reverse()) {
  console.log(myNum);
}

或者您可以使用这个备用的双向链接 Set 实现:

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);
}

于 2017-01-13T12:57:00.217 回答
4

您还可以考虑向您的 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 上找到:

于 2019-07-19T18:44:54.840 回答
3

var mySet = new Set([1,2,3,4,5]);
var reverse = [...mySet].reverse();
for(let myNum of reverse){
 console.log(myNum);
}

于 2017-01-13T12:55:41.013 回答
0

您可以制作一个数组,将其反转并生成一组新数组。

var mySet = new Set([1, 2, 3, 4, 5]),
    reverseSet = new Set([...mySet].reverse());
    
console.log([...reverseSet]);

于 2017-01-13T12:50:42.233 回答
0

您也可以使用reduceRight()for 循环并忽略返回的回调值。

  • 根据您的循环任务,reduceRight()可以比reverse()..快 20%
  • 对于较大的数据集(> 1000 个项目)reverse()+for(){}循环似乎更快..

替补席: https ://jsben.ch/8vWhD

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);

于 2021-05-20T19:03:38.830 回答