Container class serves as the base class for Deque, Set and Dict, which inherit all methods from Container, except those that are overwritten (see below). In addition, the Container and all its subclasses are iterable, that is, they provide a method returning an Iterator to iterate through the elements of the container object.
Class diagram with basic class hierarchy.
The following table shows member methods divided by class. The top half contains all Container methods, each derived by the subclasses to the right unless there is a new entry in a sub-class column, meaning the method is overwritten by the subclass. The bottom half contains methods unique to each subclass.
| Iterable | Container | Deque | Set | Dict |
|---|---|---|---|---|
iter() |
||||
add(elem) |
add(elem) |
add(key, value) |
||
apply(f) |
||||
clear() |
||||
discard(elem, right=F) |
discard(key) |
|||
empty() |
||||
has(elem) |
has(key) |
|||
print(list.len=10) |
||||
remove(elem, right=F) |
remove(key) |
|||
size() |
||||
type() |
||||
values() |
||||
| —————– | —————— | —————————— | ||
addleft(elem) |
union(s) |
get(key) |
||
count(elem) |
intersect(s) |
keys() |
||
peek() |
diff(s) |
peek(key, default=NULL) |
||
peekleft() |
is.equal(s) |
pop(key) |
||
pop() |
is.subset(s) |
popitem() |
||
popleft() |
is.superset(s) |
set(key, value, add=FALSE) |
||
reverse() |
sort(decr=FALSE) |
|||
rotate(n=1L) |
Method descriptions are found in the respective online helps (see ?Container, ?Deque, ?Set, and ?Dict).
The base Container is ready to be used by itself. Examples of Deque, Set, and Dict, follow below.
library(container)collection <- Container$new()
collection$empty()
#> [1] TRUEBy default, elements internally are stored in a basic list and therefore can be of any type.
collection$add(1)
collection$add("A")
collection$add(data.frame(B=1, C=2))
collection$type()
#> [1] "list"The internal representation can always be retrieved directly using the values function.
collection$values()
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] "A"
#>
#> [[3]]
#> B C
#> 1 1 2The container’s print method presents the content more compact similar to utils::str
collection$print() # alternatively: print(collection)
#> <Container> of 3 elements: List of 3
#> $ : num 1
#> $ : chr "A"
#> $ :'data.frame': 1 obs. of 2 variables:
#> ..$ B: num 1
#> ..$ C: num 2If initialized with an R object, the type of the object is adopted to allow for efficient internal representations, if required.
ints <- Container$new(integer())
ints$type()
#> [1] "integer"The add method supports chaining.
ints$add(1)$add(2)$add(3.7)$print()
#> <Container> of 3 elements: int [1:3] 1 2 3Initialization also works with vectors.
ints <- Container$new(1:10)$print()
#> <Container> of 10 elements: int [1:10] 1 2 3 4 5 6 7 8 9 10
ints$values()
#> [1] 1 2 3 4 5 6 7 8 9 10
ints$size()
#> [1] 10ints$has(11)
#> [1] FALSE
ints$has(7)
#> [1] TRUE
ints$discard(7)$has(7)
#> [1] FALSE
ints$remove(8)$has(8)
#> [1] FALSEUsing remove on non-existent elements throws an error,
tryCatch(ints$remove(8), error = function(e) e$message)
#> [1] "8 not in Container"but discard does not.
ints$discard(8) # okDiscard and remove work also from the right.
ints$add(1:3)$values()
#> [1] 1 2 3 4 5 6 9 10 1 2 3
ints$discard(1)$values()
#> [1] 2 3 4 5 6 9 10 1 2 3
ints$discard(2, right=TRUE)$values()
#> [1] 2 3 4 5 6 9 10 1 3unlist(ints$apply(f = function(x) x^2))
#> [1] 4 9 16 25 36 81 100 1 9
ints$clear()$empty()
#> [1] TRUEMore examples are found in the online help (see ?Container).
Being based on R6 classes, any Container object provides reference semantics.
members <- Container$new(c("Lisa", "Bob", "Joe"))$print()
#> <Container> of 3 elements: chr [1:3] "Lisa" "Bob" "Joe"
remove_Joe <- function(cont) cont$discard("Joe")
remove_Joe(members)
members
#> <Container> of 2 elements: chr [1:2] "Lisa" "Bob"it <- members$iter()
while(it$has_next()) print(it$get_next())
#> [1] "Lisa"
#> [1] "Bob"Once iterated to the last element, trying to iterate further leads to an error.
tryCatch(it$get_next(), error = function(e) e$message)
#> [1] "Iterator has no more elements."d <- Deque$new(0L)
d$type()
#> [1] "integer"
d
#> <Deque> of 1 elements: int 0d$add(1)$add(2)$addleft(1)$addleft(2)$values()
#> [1] 2 1 0 1 2
d$count(0) # count number of 0s
#> [1] 1
d$count(1) # count number of 1s
#> [1] 2A peek shows the last value, while pop shows and removes it afterwards.
d$peek()
#> [1] 2
d$pop()
#> [1] 2
d$pop()
#> [1] 1
d$values()
#> [1] 2 1 0Being a double-ended queue, both methods are also defined for the left side.
d$peekleft()
#> [1] 2
d$popleft()
#> [1] 2
d$values()
#> [1] 1 0
d$count(2)
#> [1] 0Invoking peek on an empty Deque gives NULL while pop stops with an error.
Deque$new()$peek()
#> NULL
tryCatch(Deque$new()$pop(), error = function(e) e$message)
#> [1] "pop at empty Deque"d$add(rep(0, 3))$values()
#> [1] 1 0 0 0 0
d$rotate()$values() # rotate 1 to the right
#> [1] 0 1 0 0 0
d$rotate(2)$values() # rotate 2 to the right
#> [1] 0 0 0 1 0
d$rotate(-3)$values() # rotate 3 to the left
#> [1] 1 0 0 0 0
d$addleft(4:2)$values()
#> [1] 4 3 2 1 0 0 0 0
d$reverse()$values()
#> [1] 0 0 0 0 1 2 3 4Use iterator to re-add elements left and right into second Deque object.
d2 <- Deque$new(integer())
it <- d$iter()
while(it$has_next()) {
d2$add(it$get_next())
if (it$has_next()) d2$addleft(it$get_next())
}
d2$values()
#> [1] 4 2 0 0 0 0 1 3s1 <- Set$new(1:3)$print()
#> <Set> of 3 elements: int [1:3] 1 2 3
s1$add(1) # does not change the set
s1
#> <Set> of 3 elements: int [1:3] 1 2 3s1 <- Set$new(c(1, 2, 4, 5))
s2 <- Set$new(c( 2, 3, 5, 6))
s1$union(s2)$print()
#> <Set> of 6 elements: num [1:6] 1 2 4 5 3 6
s1$intersect(s2)$print()
#> <Set> of 2 elements: num [1:2] 2 5
s1$diff(s2)$print()
#> <Set> of 2 elements: num [1:2] 1 4
s1$is.subset(s2)
#> [1] FALSE
s1$is.subset(s1$union(s2))
#> [1] TRUE
s1$intersect(s2)$is.subset(s1)
#> [1] TRUE
s1$is.equal(s2)
#> [1] FALSE
s1$is.equal(s1)
#> [1] TRUE
s1$is.superset(s2)
#> [1] FALSE
s1$union(s2)$is.superset(s2)
#> [1] TRUEDue to the key-value semantic, several Container methods are extended to take the key argument.
ages <- Dict$new(c(Peter=24, Lisa=23, Bob=32))$print()
#> <Dict> of 3 elements: Named num [1:3] 24 23 32
#> - attr(*, "names")= chr [1:3] "Peter" "Lisa" "Bob"
ages$add("Albert", 139)$values()
#> Peter Lisa Bob Albert
#> 24 23 32 139
tryCatch(ages$add("Bob", 40), error = function(e) e$message)
#> [1] "key 'Bob' already in Dict"
ages$has("Peter")
#> [1] TRUE
ages$discard("Albert")$values()
#> Peter Lisa Bob
#> 24 23 32
# Trying to discard a non-existing key has no effect
ages$discard("Albert")$values()
#> Peter Lisa Bob
#> 24 23 32
# Trying to remove a non-existing key throws an error
tryCatch(ages$remove("Albert"), error = function(e) e$message)
#> [1] "key 'Albert' not in Dict"ages$keys()
#> [1] "Peter" "Lisa" "Bob"
ages$peek("Lisa")
#> [1] 23
ages$peek("Anna")
#> NULLTrying to set a value at a non-existing key throws an error unless the set method is explicitly told to add it to the Dict.
tryCatch(ages$set("Anna"), error = function(e) e$message)
#> [1] "key 'Anna' not in Dict"
ages$set("Anna", 23, add=TRUE) # alternatively ages$add("Anna", 23)
ages
#> <Dict> of 4 elements: Named num [1:4] 24 23 32 23
#> - attr(*, "names")= chr [1:4] "Peter" "Lisa" "Bob" "Anna"This allows fine control over the insert-behaviour of the Dict. If already existing, the value is overwritten.
ages$set("Lisa", 11)$values()
#> Peter Lisa Bob Anna
#> 24 11 32 23A similar control is provided via the different methods to retrieve elements.
ages$pop("Lisa")
#> [1] 11
ages$values()
#> Peter Bob Anna
#> 24 32 23
tryCatch(ages$pop("Lisa"), error = function(e) e$message)
#> [1] "key 'Lisa' not in Dict"
tryCatch(ages$get("Lisa"), error = function(e) e$message)
#> [1] "key 'Lisa' not in Dict"
ages$peek("Lisa")
#> NULLFinally, the Dict could also be used as a sampler (without replacement).
set.seed(123)
while(!ages$empty()) {
print(ages$popitem())
}
#> Peter
#> 24
#> Anna
#> 23
#> Bob
#> 32