Hash Table and Hash Set

The hashtable packages provides three implementations of hash tables and hash maps:

  1. using std::unordered_map and std::unordered_set from C++, in functions hash_table() and hash_set(),
  2. using the fastmatch package, in functions hash_fm_table() and hash_fm_set(),
  3. using environment, in functions hash_env_table() and hash_env_set().

They share the same user interface.

Hash table

Three ways to create hash tables:

library(hashtable)
h1 = hash_table(letters, 1:26)
h2 = hash_fm_table(letters, 1:26)
h3 = hash_env_table(letters, 1:26)
h1
## A hash table [hash_unordered_map] with 26 key-value (integer) pairs
##   w => 23
##   u => 21
##   z => 26
##   ......
##   c => 3
##   b => 2
##   a => 1
h2
## A hash table [hash_fm_table] with 26 key-value (integer) pairs
##   a => 1
##   b => 2
##   c => 3
##   ......
##   x => 24
##   y => 25
##   z => 26
h3
## A hash table [hash_env_table] with 26 key-value (integer) pairs
##   i => 9
##   j => 10
##   k => 11
##   ......
##   f => 6
##   g => 7
##   h => 8

The user interfaces for the two methods are the same. We only demonstrate using h1.

Get all keys:

hash_keys(h1)
##  [1] "w" "u" "z" "v" "t" "p" "o" "n" "l" "r" "q" "k" "j" "m" "i" "y" "s" "h" "g"
## [20] "f" "e" "d" "x" "c" "b" "a"

Get all values:

hash_values(h1)
##  [1] 23 21 26 22 20 16 15 14 12 18 17 11 10 13  9 25 19  8  7  6  5  4 24  3  2
## [26]  1

Get a subset of values by specifying keys:

hash_values(h1, c("a", "b", "c"))
## [1] 1 2 3

Get a single value using $, [[ or [:

h1$a
## [1] 1
h1[["a"]]
## [1] 1
h1[c("a", "b")]
## [1] 1 2

Test whether keys are in the hash table:

hash_exists(h1, c("a", "b", "foo"))
## [1]  TRUE  TRUE FALSE

Delete key-value pairs:

hash_delete(h1, c("a", "b"))
hash_exists(h1, c("a", "b"))
## [1] FALSE FALSE

Insert new key-value pairs, or modify key-value pairs if they already exist:

hash_insert(h1, "c", 100L); h1$c
## [1] 100
hash_insert(h1, "c", -1L); h1$c
## [1] -1
hash_insert(h1, "foo", 0L); h1$foo
## [1] 0

Insert or modify key-value pairs using $<-, [[<- or [<-:

h1$a = 20L; h1$a
## [1] 20
h1[["bar"]] = -100L; h1$bar
## [1] -100
h1[c("c", "d", "e")] = c(-1L, -2L, -3L); h1[c("c", "d", "e")]
## [1] -1 -2 -3

In previous examples, values are atomic vectors. It is basically the same if values are more general list.

h1 = hash_table(c("a", "b"), list(1L, "text"))
h1
## A hash table [hash_unordered_map] with 2 key-value (list) pairs
##   b => character [1] text
##   a => integer [1] 1
h1$c = 3.14
h1$d = lm(1~1) # an lm object
h1
## A hash table [hash_unordered_map] with 4 key-value (list) pairs
##   d => complex value (lm)
##   b => character [1] text
##   a => integer [1] 1
##   c => numeric [1] 3.14

Convert between hash table and named vector or list:

h1 = hash_table(c("a", "b"), 1:2)
as.vector(h1)
## b a 
## 2 1
h1 = hash_table(c("a", "b"), list(1L, "text"))
as.vector(h1)
## $b
## [1] "text"
## 
## $a
## [1] 1
vec = structure(1:2, names = c("a", "b"))
as.hash_table(vec)
## A hash table [hash_unordered_map] with 2 key-value (integer) pairs
##   b => 2
##   a => 1
lt = structure(list(1L, "text"), names = c("a", "b"))
as.hash_table(lt)
## A hash table [hash_unordered_map] with 2 key-value (list) pairs
##   b => character [1] text
##   a => integer [1] 1

Hash set

Hash sets are hash tables with no value associated. There are also three ways to create hash sets.

h1 = hash_set(letters)
h2 = hash_fm_set(letters)
h3 = hash_env_set(letters)
h1
## A hash set [hash_unordered_set] with 26 keys

Get all keys:

hash_keys(h1)
##  [1] "w" "u" "z" "v" "t" "p" "o" "n" "l" "r" "q" "k" "j" "m" "i" "y" "s" "h" "g"
## [20] "f" "e" "d" "x" "c" "b" "a"

Hash set has no value associated, so calling hash_value() throws an error.

hash_values(h1)
## Error in `hash_values()`:
## ! hash_unordered_set has no values.

Test whether keys are in the hash set:

hash_exists(h1, c("a", "foo"))
## [1]  TRUE FALSE

Add new keys:

hash_insert(h1, "foo")
hash_exists(h1, "foo")
## [1] TRUE

Delete keys:

hash_delete(h1, "foo")
hash_exists(h1, "foo")
## [1] FALSE

Hash sets are basically used to test whether keys exist, so we let $, [[ and [ to return TRUE or FALSE to represent whether keys exist.

h1$a
## [1] TRUE
h1[["a"]]
## [1] TRUE
h1$foo
## [1] FALSE
h1[c("a", "b", "foo")]
## [1]  TRUE  TRUE FALSE

If assigning TRUE, new keys are added to the hash set (if they do not exist), and if assigning FALSE, corresponding keys are deleted.

h1$a = FALSE
hash_exists(h1, "a")
## [1] FALSE
h1$foo = TRUE
hash_exists(h1, "foo")
## [1] TRUE

Convert between vectors (where elements are unique) and hash sets:

as.vector(h1)
##  [1] "foo" "w"   "u"   "z"   "v"   "t"   "p"   "o"   "n"   "l"   "r"   "q"  
## [13] "k"   "j"   "m"   "i"   "y"   "s"   "h"   "g"   "f"   "e"   "d"   "x"  
## [25] "c"   "b"
as.hash_set(letters)
## A hash set [hash_unordered_set] with 26 keys

hash_fm_table and hash_fm_set

Although sharing the same user interface, the hash table created by hash_fm_table() allows to modify the values if corresponding keys already exist.

h = hash_fm_table(letters, 1:26)
h$a = 100L

But it does not allow to delete or create key-value pairs.

h$foo = 1L  # insert new key foo
## Error in `.local()`:
## ! hash_fm_table is not allowed to insert new keys.
hash_delete(h, "a")
## Error in `hash_delete()`:
## ! hash_fm is not allowed to modify.

The hash set created by hash_fm_set() does not allow to delete or create keys.

h = hash_fm_set(letters)
h$a = FALSE
## Error in `hash_delete()`:
## ! hash_fm is not allowed to modify.
h$foo = TRUE
## Error in `.local()`:
## ! hash_fm_set is not allowed to modify.

Session info

sessionInfo()
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] hashtable_1.0.0 rmarkdown_2.31 
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.39    R6_2.6.1         fastmatch_1.1-8  fastmap_1.2.0   
##  [5] xfun_0.60        maketools_1.3.2  cachem_1.1.0     knitr_1.51      
##  [9] htmltools_0.5.9  buildtools_1.0.0 lifecycle_1.0.5  cli_3.6.6       
## [13] sass_0.4.10      jquerylib_0.1.4  compiler_4.6.1   sys_3.4.3       
## [17] tools_4.6.1      evaluate_1.0.5   bslib_0.11.0     Rcpp_1.1.2      
## [21] yaml_2.3.12      jsonlite_2.0.0   rlang_1.3.0