Priority queue basics
priority_queue is a persistent structure that associates
elements with priority values, typically numeric or character (string).
They provide fast access to elements by min or max priority, and
insertion of new elements with given priorities.
All operations are persistent, and return modified copies. Peeking at
the min/max returns the stored element (which may be any type), popping
returns a list with $value containing the stored element,
$priority the priority, and $remaining the
rest of the priority queue without those elements.
x <- priority_queue("task_a", "task_b", "task_c", priorities = c(3, 1, 2))
x
#> Unnamed priority_queue with 3 elements.
#> Minimum priority: 1, Maximum priority: 3
#>
#> Elements (by priority):
#>
#> (priority 1)
#> [1] "task_b"
#>
#> (priority 2)
#> [1] "task_c"
#>
#> (priority 3)
#> [1] "task_a"
peek_min(x)
#> [1] "task_b"
res <- pop_max(x)
res$value
#> [1] "task_a"
res$priority
#> [1] 3
res$remaining
#> Unnamed priority_queue with 2 elements.
#> Minimum priority: 1, Maximum priority: 2
#>
#> Elements (by priority):
#>
#> (priority 1)
#> [1] "task_b"
#>
#> (priority 2)
#> [1] "task_c"The as_priority_queue() variant builds a queue from a
vector or list of elements paired with a priority vector. useful when
priorities are already in a separate vector.
q <- as_priority_queue(letters[1:4], priorities = c(3, 1, 2, 1))
q
#> Unnamed priority_queue with 4 elements.
#> Minimum priority: 1, Maximum priority: 3
#>
#> Elements (by priority):
#>
#> (priority 1)
#> [1] "b"
#>
#> (priority 1)
#> [1] "d"
#>
#> (priority 2)
#> [1] "c"
#>
#> (priority 3)
#> [1] "a"Priority ties
In the case of multiple identical priorities, peek/pop for min/max
return a single element, in their original insertion order.
Their “all” counterparts (peek_all_min,
pop_all_max, etc.) return two priorities: in
$elements the elements peeked/popped and in
$remaining the other elements. peek_all_max
and pop_all_max behave symmetrically on the
maximum-priority tie run.
y <- priority_queue() |>
insert("Jones", priority = "B") |>
insert("Smith", priority = "A") |>
insert("Adams", priority = "A")
peek_min(y)
#> [1] "Smith"
peek_all_min(y)
#> Unnamed priority_queue with 2 elements.
#> Minimum priority: A, Maximum priority: A
#>
#> Elements (by priority):
#>
#> (priority A)
#> [1] "Smith"
#>
#> (priority A)
#> [1] "Adams"
res1 <- pop_min(y)
res1$value
#> [1] "Smith"
res1$priority
#> [1] "A"
res1$remaining
#> Unnamed priority_queue with 2 elements.
#> Minimum priority: A, Maximum priority: B
#>
#> Elements (by priority):
#>
#> (priority A)
#> [1] "Adams"
#>
#> (priority B)
#> [1] "Jones"
res2 <- pop_all_min(y)
res2$elements
#> Unnamed priority_queue with 2 elements.
#> Minimum priority: A, Maximum priority: A
#>
#> Elements (by priority):
#>
#> (priority A)
#> [1] "Smith"
#>
#> (priority A)
#> [1] "Adams"
res2$remaining
#> Unnamed priority_queue with 1 element.
#> Minimum priority: B, Maximum priority: B
#>
#> Elements (by priority):
#>
#> (priority B)
#> [1] "Jones"Priority queues can be converted to R lists with
as.list(). The drop_meta parameter defaulting
to FALSE controls whether priority values are included.
Empty queues
Peek/pop helpers are non-throwing on empty queues,
length() allows for empty checking.
empty_q <- priority_queue()
length(empty_q)
#> [1] 0
peek_min(empty_q)
#> NULL
pop_min(empty_q)
#> $value
#> NULL
#>
#> $priority
#> NULL
#>
#> $remaining
#> Unnamed priority_queue with 0 elements.Priority values
min_priority() and max_priority() return
the current extrema priority (not the stored element), in O(1)
via cached monoid state.
q <- priority_queue("a", "b", "c", priorities = c(3, 1, 2))
min_priority(q)
#> [1] 1
max_priority(q)
#> [1] 3
min_priority(priority_queue()) # NULL when empty
#> NULLNamed priorities
Priority queues can carry names, set either at construction or on
insert(). Names support read-only indexing via
[, [[, and $. This is the
only indexing form priority_queue supports.
Positional indexing and all replacement forms ([<-,
[[<-, $<-) error; cast to
as_flexseq() first to mutate.
q <- priority_queue(a = "task-a", b = "task-b", priorities = c(2, 1)) |>
insert("task-c", priority = 3, name = "c")
q[["b"]]
#> [1] "task-b"
q[c("a", "c")]
#> Named priority_queue with 2 elements.
#> Minimum priority: 2, Maximum priority: 3
#>
#> Elements (by priority):
#>
#> $a (priority 2)
#> [1] "task-a"
#>
#> $c (priority 3)
#> [1] "task-c"
try(q[1]) # positional indexing blocked
#> Error in `[.priority_queue`(q, 1) :
#> `[.priority_queue` supports character name indexing only. Cast first with `as_flexseq()`.
try(q$a <- "!!") # replacement blocked
#> Error in `$<-.priority_queue`(`*tmp*`, a, value = "!!") :
#> `$<-` is not supported for priority_queue. Cast first with `as_flexseq()`.Transforming, iterating, merging
fapply() maps a function over queue elements while
preserving priorities. The function receives
(value, priority), or (value, priority, name)
if it accepts a third argument — priorities and names are passed in
read-only, and the return value replaces the stored element.
q <- priority_queue("alice", "bob", "carol", priorities = c(3, 1, 2))
fapply(q, function(value, priority) toupper(value))
#> Unnamed priority_queue with 3 elements.
#> Minimum priority: 1, Maximum priority: 3
#>
#> Elements (by priority):
#>
#> (priority 1)
#> [1] "BOB"
#>
#> (priority 2)
#> [1] "CAROL"
#>
#> (priority 3)
#> [1] "ALICE"loop() (re-exported from the coro
package) walks the queue in priority-ascending order, yielding bare
values. Traversal is driven by repeated pop_min(), so full
traversal is
(
per step); ties within equal priority follow insertion order.
q <- priority_queue("task_a", "task_b", "task_c", priorities = c(3, 1, 2))
loop(for (v in q) print(v))
#> [1] "task_b"
#> [1] "task_c"
#> [1] "task_a"Use fapply() if your callback needs the priority
alongside the value, or cast with as_flexseq() for
insertion-order iteration. Plain for (v in q) (without
loop()) yields raw finger-tree internals rather than
elements — always wrap with loop().
merge(x, y) combines two priority queues into a new one
containing every entry from both, in O(log(min(m, n))). The
.pq_min / .pq_max monoids recompute
automatically on the merged tree, so extremum queries work immediately
on the result.
a <- priority_queue("x", "y", priorities = c(5, 1))
b <- priority_queue("z", priorities = 3)
m <- merge(a, b)
length(m)
#> [1] 3
peek_min(m)
#> [1] "y"
peek_max(m)
#> [1] "x"Both queues must share the same priority type and monoid set; mismatches error (rather than being silently harmonized). Both inputs are left unmodified.
For full sequence-style operations, cast with
as_flexseq(). This returns payload items only; priority
metadata and any custom monoids are dropped. To instead obtain a
flexseq of entry records (value +
priority), compose with as.list().
y <- priority_queue() |>
insert("Jones", priority = "B") |>
insert("Smith", priority = "A") |>
insert("Adams", priority = "A")
as_flexseq(y)
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> [1] "Jones"
#>
#> [[2]]
#> [1] "Smith"
#>
#> [[3]]
#> [1] "Adams"
as_flexseq(as.list(y))
#> Unnamed flexseq with 3 elements.
#>
#> Elements:
#>
#> [[1]]
#> $value
#> [1] "Jones"
#>
#> $priority
#> [1] "B"
#>
#>
#> [[2]]
#> $value
#> [1] "Smith"
#>
#> $priority
#> [1] "A"
#>
#>
#> [[3]]
#> $value
#> [1] "Adams"
#>
#> $priority
#> [1] "A"