The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

keylist

Lightweight list extensions that enforce unique keys, ensuring predictable key-value access.

Installation

Install the latest release of keylist from CRAN.

install.packages("keylist")

You can install the development version from GitHub.

# install.packages("pak")
pak::pak("LJ-Jenkins/keylist")

Overview

keylist provides two lightweight keylist S3 classes klist and knlist: extensions of list that enforce unique keys. klist accepts both unnamed and named elements, while knlist only accepts named elements.

knlist behaves similarly to Python dictionaries/dicts and can serve as a more idiomatic R, copy-on-modify alternative without reference semantics.

library(keylist)

# klist - unnamed or named (if names present, they must be unique).
klist(1, a = 2)
#> <keylist::klist>
#> [[1]]
#> [1] 1
#> 
#> $a
#> [1] 2
klist(1, a = 1, a = 2, b = 1, b = 2)
#> Error in `klist()`:
#> ! Names must be unique.
#> Duplicate names: a, b

# knlist - fully named and unique.
knlist(a = 1, b = 2)
#> <keylist::knlist>
#> $a
#> [1] 1
#> 
#> $b
#> [1] 2
knlist(1, b = 2)
#> Error in `knlist()`:
#> ! All elements must be named.
knlist(a = 1, a = 2, b = 1, b = 2)
#> Error in `knlist()`:
#> ! Names must be unique.
#> Duplicate names: a, b

# keylist as more general function.
class(keylist(1))
#> [1] "klist"
class(keylist(a = 1, .named = TRUE))
#> [1] "knlist"

As extensions to list, keylists can take any input list can. Inputs lists are not validated as they are not keylists - keylist validation operates on its own level so for validation of nested elements they must also be keylists.

# input list separate entity.
keylist(1, list(a = 1, a = 1))
#> <keylist::klist>
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [[2]]$a
#> [1] 1
#> 
#> [[2]]$a
#> [1] 1

# each keylist validates its own level.
keylist(1, keylist(a = 1, a = 1))
#> Error in `klist()`:
#> ! Names must be unique.
#> Duplicate names: a

All nested elements of a list can be recursively turned into a keylist when using one of the as.* variants.

x <- list(1, list(a = 1, a = 1))
as.keylist(x, .recursive = TRUE)
#> Error in `as.klist.list()`:
#> ! Names must be unique.
#> Duplicate names: a

Assignment and Modification

Although lists in R can accept duplicate names, R preferentially chooses the existing list’s name structure when it comes to assignment. Therefore if you can guarantee that the keys are unique at creation, there is no way of assigning a duplicate key using the normal R methods of [, [[ and $. See this example:

x <- list(1, a = 2, 3, b = 4)
x[1] <- list(x = 1)
x[1] # remains unnamed.
#> [[1]]
#> [1] 1

x["a"] <- list(x = 1)
x[2] # name remains 'a'.
#> $a
#> [1] 1

x[5] <- list(k = 10) # index 5 doesn't exist...
x[5] # yet still remains unnamed.
#> [[1]]
#> [1] 10

keylist makes use of the above by not implementing any special validation tricks - it simply ensures the keys are unique upon creation then let’s you go on your way.

However, assigning by position could enable a non-named element for a knlist. To circumvent this (and to give a clearer definition of type), knlist objects only allow assignment when the indexing is done by name.

x <- knlist(a = 1)
x[[1]] <- 1
#> Error:
#> ! Only character indexing is allowed for assignment into knlist objects

# extraction by index is allowed.
x[[1]]
#> [1] 1

After creation, the main way that duplicate names may creep into lists is with direct name editing with names() or with list concatenation with c(). keylist includes S3 methods for both klist and knlist for these functions to ensure that duplicate keys are not added from these methods.

x <- klist(1, a = 1)
names(x) <- c("a", "a")
#> Error in `names<-.klist`:
#> ! Names must be unique.
#> Duplicate names: a

x <- knlist(a = 1)
# name removal allowed for klist, but not knlist.
names(x) <- NULL
#> Error in `names<-.knlist`:
#> ! Names cannot be removed from a knlist object.

# knlist doesn't accept "" or NA names (like klist does).
setNames(x, NA)
#> Error in `names<-.knlist`:
#> ! Names cannot be <NA>.

class(c(klist(1, a = 2), 2, list(3)))
#> [1] "klist"
c(klist(1, a = 2), a = 2, list(3))
#> Error in `c.klist()`:
#> ! Names must be unique.
#> Duplicate names: a

Note

All name comparison is done using C’s strcmp function.

Getting help

If you encounter a clear bug, please file an issue with a minimal reproducible example on GitHub.

Code of Conduct

Please note that the keylist project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.