如何将Javascript对象/地图用作队列

时间:2015-11-01 00:20:09

标签: javascript arrays dictionary queue

现在我有一个队列(JS数组),用于存储等待游戏的玩家。我需要队列的FIFO属性,以便首先添加到队列中的玩家首先进入新游戏。队列的问题在于它没有恒定的时间查找。如果我能有一张跟踪插入顺序的地图会很棒(我知道依靠地图来做这件事是JS不可靠)。如果我为属性赋予其插入顺序的值,则需要在有人离开队列时更新它,因此这也没有用。无论如何围绕这个?获取常量查找和维护插入顺序的方法?

2 个答案:

答案 0 :(得分:3)

If you don't have memory constraints, maybe you can maintain a map with the queue implemented as a double linked list. Here is a sample implementation:

function Queue() {
    var oldestRequest,
        newestRequest,
        map = {};

    this.addUser = function(userID) {
        var newRequest = { userID: userID };
        map[userID] = newRequest;

        // Set this as the oldest request if it is the first request
        if (!oldestRequest) {
            oldestRequest = newRequest;
        }

        // If this isn't the first request, add it to the end of the list
        if (newestRequest) {
            newestRequest.next = newRequest;
            newRequest.previous = newestRequest;
        }

        newestRequest = newRequest;
    };

    this.nextUser = function() {
        // If we don't have any requests, undefined is returned
        if (oldestRequest) {
           var request = oldestRequest;
           oldestRequest = request.next;
           delete map[request.userID];

           // Make sure we don't hang on to references to users
           // that are out of the queue
           if (oldestRequest) {
               delete oldestRequest.previous;
           }

           // This is the last request in the queue so "empty" it
           if (request === newestRequest) {
               newestRequest = undefined;
           }

           return request;
        }
    };

    this.removeUser = function(userID) {
        var request = map[userID];
        delete map[userID];

        if (request.previous) {
            request.previous.next = request.next;
        }

        if (request.next) {
            request.next.previous = request.previous;
        }
    };

    return this;
}

答案 1 :(得分:0)

您可以将地图与队列结合使用以提供恒定时间访问。下面是 TypeScript 4.2 中的实现。使用 Map 代替 Object 来提供 better performance 附加值和删除值。

// TypeScript typing
export type KeyValuePair<K, V> = [ K, V ]

interface ValueData<V> {
  value: V
  refCount: number
}

// Public classes
export class MapQueue<K, V> {
  readonly #queue: Array<KeyValuePair<K, V>>
  readonly #map: Map<K, ValueData<V>>

  constructor () {
    this.#queue = []
    this.#map = new Map()
  }

  get length (): number {
    return this.#queue.length
  }

  unshiftOne (pair: KeyValuePair<K, V>): number {
    const [key, value] = pair
    const valueData = this.#map.get(key)
    if (valueData !== undefined) {
      if (valueData.value !== value) {
        throw new Error(`Key ${String(key)} with different value already exists`)
      }
      valueData.refCount++
    } else {
      this.#map.set(key, {
        value,
        refCount: 1
      })
    }
    return this.#queue.unshift(pair)
  }

  pop (): KeyValuePair<K, V> | undefined {
    const result = this.#queue.pop()
    if (result !== undefined) {
      const valueData = this.#map.get(result[0])
      if (valueData !== undefined) {
        valueData.refCount--
        if (valueData.refCount === 0) {
          this.#map.delete(result[0])
        }
      }
    }
    return result
  }

  get (key: K): V | undefined {
    return this.#map.get(key)?.value
  }
}