Files
foundryvtt-drag-ruler/js/data_structures.js
T
2022-03-09 18:40:43 +01:00

139 lines
3.3 KiB
JavaScript

/**
* A combination queue/set where the elements are ordered (in ascending order, according to the given priority function)
* and unique (according to the given elementMatcher).
*
* If an element is added to the set and an equivalent element already exists, the lower-priority one is discarded.
*/
export class PriorityQueueSet {
constructor(elementMatcher, priorityFunction) {
this.first = null;
this.elementMatcher = elementMatcher;
this.priorityFunction = priorityFunction;
}
pushWithPriority(value) {
const newNode = {value, priority: this.priorityFunction(value), next: null};
// If the queue is currently empty, we can just set this new node as the first and we're done
if (!this.first) {
this.first = newNode;
return;
}
let inserted = false;
let previous;
let current = this.first;
// Loop through the existing elements
while (current) {
if (this.elementMatcher(current.value, value)) {
// We've found an equivalent element before one with a lower priority. This one has at least
// the same priority as the new one, so don't bother inserting
return;
} else if (newNode.priority <= current.priority) {
// We've found some element with lower priority than the new one, so insert the new one just before it
newNode.next = current;
if (previous) {
previous.next = newNode;
} else {
this.first = newNode;
}
inserted = true;
previous = current;
current = current.next;
break;
}
previous = current;
current = current.next;
}
if (inserted) {
// Go through the rest of the list and try to find an equivalent element to the new one.
// We know it has higher priority than the new one, so remove it.
while (current) {
if (this.elementMatcher(current.value, value)) {
if (previous) {
previous.next = current.next;
} else {
this.first = current.next;
}
return;
}
previous = current;
current = current.next;
}
} else {
// We reached the end of the queue without finding a lower-priority or existing element, so
// insert the new one at the end
previous.next = newNode;
}
}
hasNext() {
return !!this.first;
}
pop() {
const first = this.first;
this.first = first?.next;
return first?.value;
}
}
/**
* Queue that will only ever accept elements with a given value once. Elements must have a "value" field, the
* JSON representation of which will be used as the key to match
*/
export class ProcessOnceQueue {
constructor() {
this.first = null;
this.last = null;
this.previouslyQueued = new Set();
}
/**
* Remove everything from the queue and forget all the previously-queued items
*/
reset() {
this.first = null;
this.last = null;
this.previouslyQueued.clear();
}
push(element) {
if (this.previouslyQueued.has(element)) {
return;
}
this.previouslyQueued.add(element);
const newNode = {
value: element,
next: null,
previous: null
}
if (!this.first) {
this.first = newNode;
this.last = newNode;
} else {
this.last.next = newNode;
newNode.previous = this.last;
this.last = newNode;
}
}
pop() {
const node = this.first;
this.first = node?.next;
if (!node?.next) {
this.last = null;
}
return node?.value;
}
hasNext() {
return !!this.first;
}
}