diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b9257..6cce0e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- `.fix()` to repair queue positions after mutable priorities change. + ## [6.3.5] - 2025-10-12 ### Fixed - Allows creating queues from list of values in O(n) runtime via constructor & fromArray diff --git a/src/maxPriorityQueue.d.ts b/src/maxPriorityQueue.d.ts index 5c0f5bd..0b50baf 100644 --- a/src/maxPriorityQueue.d.ts +++ b/src/maxPriorityQueue.d.ts @@ -5,6 +5,7 @@ import { LegacyOptions } from './minPriorityQueue'; export interface MaxPriorityQueue extends PriorityQueue { enqueue(value: T): MaxPriorityQueue; push(value: T): MaxPriorityQueue; + fix(): MaxPriorityQueue; } export const MaxPriorityQueue: { diff --git a/src/minPriorityQueue.d.ts b/src/minPriorityQueue.d.ts index 3ac4475..69c36a6 100644 --- a/src/minPriorityQueue.d.ts +++ b/src/minPriorityQueue.d.ts @@ -8,6 +8,7 @@ export interface LegacyOptions { export interface MinPriorityQueue extends PriorityQueue { enqueue(value: T): MinPriorityQueue; push(value: T): MinPriorityQueue; + fix(): MinPriorityQueue; } export const MinPriorityQueue: { diff --git a/src/priorityQueue.d.ts b/src/priorityQueue.d.ts index c3a5f98..0c79b01 100644 --- a/src/priorityQueue.d.ts +++ b/src/priorityQueue.d.ts @@ -12,6 +12,7 @@ export interface PriorityQueue extends Iterable { pop(): T | null; remove(cb: (value: T) => boolean): T[]; contains(cb: (value: T) => boolean): boolean; + fix(): PriorityQueue; toArray(): T[]; clear(): void; } diff --git a/src/priorityQueue.js b/src/priorityQueue.js index 1bc876f..31d0a60 100644 --- a/src/priorityQueue.js +++ b/src/priorityQueue.js @@ -148,6 +148,16 @@ class PriorityQueue { return this._heap.isEmpty(); } + /** + * Fixes element positions in the queue + * @public + * @returns {PriorityQueue} + */ + fix() { + this._heap.fix(); + return this; + } + /** * Clears the queue * @public diff --git a/test/PriorityQueue.test.js b/test/PriorityQueue.test.js index 6462dcb..7dfdb4b 100644 --- a/test/PriorityQueue.test.js +++ b/test/PriorityQueue.test.js @@ -129,6 +129,40 @@ describe('PriorityQueue', () => { }); }); + describe('fix', () => { + it('fixes element positions when multiple priorities change', () => { + const one = { id: 'one', priority: 1 }; + const two = { id: 'two', priority: 2 }; + const three = { id: 'three', priority: 3 }; + const four = { id: 'four', priority: 4 }; + const five = { id: 'five', priority: 5 }; + const six = { id: 'six', priority: 6 }; + const seven = { id: 'seven', priority: 7 }; + const qTest = PriorityQueue.fromArray( + [one, two, three, four, five, six, seven], + (a, b) => a.priority - b.priority + ); + + one.priority = 8; + four.priority = 0; + seven.priority = 3.5; + + expect(qTest.fix()).to.equal(qTest); + expect(qTest.size()).to.equal(7); + expect(qTest.toArray()).to.eql([ + four, + two, + three, + seven, + five, + six, + one + ]); + expect(qTest.front()).to.equal(four); + expect(qTest.back()).to.equal(one); + }); + }); + describe('iterator', () => { it('allows iterating on queue elements', () => { const testArr = [20, 30, 40, 50, 80, 90]; diff --git a/test/maxPriorityQueue.test.js b/test/maxPriorityQueue.test.js index eb8024a..9180e4e 100644 --- a/test/maxPriorityQueue.test.js +++ b/test/maxPriorityQueue.test.js @@ -127,6 +127,40 @@ describe('MaxPriorityQueue', () => { expect(qTest.pop()).to.eql(20); }); }); + + describe('fix', () => { + it('fixes element positions when multiple priorities change', () => { + const one = { id: 'one', priority: 1 }; + const two = { id: 'two', priority: 2 }; + const three = { id: 'three', priority: 3 }; + const four = { id: 'four', priority: 4 }; + const five = { id: 'five', priority: 5 }; + const six = { id: 'six', priority: 6 }; + const seven = { id: 'seven', priority: 7 }; + const qTest = MaxPriorityQueue.fromArray( + [one, two, three, four, five, six, seven], + (value) => value.priority + ); + + one.priority = 8; + four.priority = 0; + seven.priority = 3.5; + + expect(qTest.fix()).to.equal(qTest); + expect(qTest.size()).to.equal(7); + expect(qTest.toArray()).to.eql([ + one, + six, + five, + seven, + three, + two, + four + ]); + expect(qTest.front()).to.equal(one); + expect(qTest.back()).to.equal(four); + }); + }); }); describe('constructor with initial values', () => { diff --git a/test/minPriorityQueue.test.js b/test/minPriorityQueue.test.js index 30c80e6..447ab47 100644 --- a/test/minPriorityQueue.test.js +++ b/test/minPriorityQueue.test.js @@ -111,7 +111,7 @@ describe('MinPriorityQueue', () => { expect(qTest.contains((n) => n === 100)).to.equal(false); }); }); - + describe('remove', () => { it('remove elements that match a criteria', () => { const testArr = [20, 30, 40, 50, 80, 90]; @@ -124,6 +124,39 @@ describe('MinPriorityQueue', () => { }); }); + describe('fix', () => { + it('fixes element positions when multiple priorities change', () => { + const one = { id: 'one', priority: 1 }; + const two = { id: 'two', priority: 2 }; + const three = { id: 'three', priority: 3 }; + const four = { id: 'four', priority: 4 }; + const five = { id: 'five', priority: 5 }; + const six = { id: 'six', priority: 6 }; + const seven = { id: 'seven', priority: 7 }; + const qTest = MinPriorityQueue.fromArray( + [one, two, three, four, five, six, seven], + (value) => value.priority + ); + + one.priority = 8; + four.priority = 0; + seven.priority = 3.5; + + expect(qTest.fix()).to.equal(qTest); + expect(qTest.size()).to.equal(7); + expect(qTest.toArray()).to.eql([ + four, + two, + three, + seven, + five, + six, + one + ]); + expect(qTest.front()).to.equal(four); + expect(qTest.back()).to.equal(one); + }); + }); }); describe('constructor with initial values', () => {