├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── NAMESPACE ├── NEWS.md ├── R ├── Class-hash.R ├── as.data.frame.hash.R ├── attach_hash.R ├── clear.R ├── copy.R ├── del.R ├── extract.R ├── format.R ├── has_key.R ├── hash-package.R ├── hash.R ├── head.R ├── invert.R ├── is.empty.R ├── keys.R ├── kv.R ├── length.R ├── make_keys.R ├── set.R ├── setkeys.R ├── show.R ├── values.R └── zzz.R ├── README.md ├── TODO.md ├── cran-comments.md ├── dev ├── hash.actions.R.off └── hash.actions.Rd.off ├── inst └── memory-test.r ├── man ├── as.data.frame.hash.Rd ├── attach_hash.Rd ├── clear.Rd ├── copy.Rd ├── del.Rd ├── extract.Rd ├── format.Rd ├── has_key.Rd ├── hash-class.Rd ├── hash-package.Rd ├── hash.Rd ├── head.Rd ├── invert.Rd ├── is.empty.Rd ├── keys.Rd ├── kv.Rd ├── make_keys.Rd ├── setkeys.Rd └── values.Rd ├── tests ├── testthat.R └── testthat │ ├── test-accessors.r │ ├── test-as.list.r │ ├── test-clear.r │ ├── test-copy.r │ ├── test-has.key.r │ ├── test-hash.r │ ├── test-issues.r │ └── test-set.r └── vignettes ├── benchmarks.Rmd └── using-hash.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^Meta$ 2 | ^doc$ 3 | ^.git/.* 4 | .git/ 5 | .git/.* 6 | 7 | ^.*\.Rproj$ 8 | ^\.Rproj\.user$ 9 | # *.Rproj 10 | dev 11 | TODO 12 | cran-comments.md 13 | vignettes 14 | ^revdep$ 15 | hash_*.tar.gz 16 | hash.Rcheck 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Meta 2 | doc 3 | *.Rproj 4 | .Rproj.user 5 | .Rhistory 6 | .RData 7 | inst/doc 8 | vignettes/benchmarks_cache/ 9 | vignettes/benchmarks_files/ 10 | 11 | hash.Rcheck/ 12 | hash_3.3.0.tar.gz 13 | revdep/ 14 | 15 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: hash 2 | Type: Package 3 | Title: A Hash/Dictionary/Map/Associated Array for R 4 | Version: 3.3.1 5 | Date: 2019-01-05 6 | Authors@R: c( 7 | person("Christopher", "Brown", , "chris.brown@decisionpatterns.com", c("aut", "cre") ), 8 | person("Decision Patterns", role = "cph") 9 | ) 10 | URL: 11 | https://github.com/decisionpatterns/r-hash 12 | http://www.decisionpatterns.com 13 | BugReports: 14 | https://github.com/decisionpatterns/r-hash/issues 15 | Depends: 16 | R (>= 3.1.0) 17 | Imports: 18 | methods (>= 3.5.0), 19 | utils 20 | Suggests: 21 | magrittr (>= 1.5), 22 | testthat (>= 1.0), 23 | knitr (>= 1.14), 24 | rmarkdown, 25 | ggplot2, 26 | microbenchmark, 27 | scales 28 | Description: A data structure similar to dictionaries in Python or hashes in 29 | other languages is implemented adapted to R's accessors syntax and 30 | vectorized operations. For objects of appreciable size, hashes outperform 31 | native named lists and vectors. A comprehensive set of functions, 32 | methods and objects make hashes easy-to-use. 33 | LazyLoad: yes 34 | License: GPL (>= 3) | file LICENSE 35 | VignetteBuilder: knitr 36 | RoxygenNote: 6.1.1 37 | Encoding: UTF-8 38 | Repository: CRAN 39 | Roxygen: list(markdown = TRUE) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This package is licenses under GPLv3. For a copy of this license, refer to 2 | http://www.r-project.org/Licenses/GPL-3. 3 | 4 | If you are interested in other licensing arrangements, please contact the 5 | copyright holder. 6 | 7 | Copyright (c) 2015-2019 Decision Patterns. Oakland, CA, USA. 8 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(as.data.frame,hash) 4 | S3method(as.list,hash) 5 | S3method(has_key,hash) 6 | S3method(head,hash) 7 | S3method(kv,hash) 8 | S3method(tail,hash) 9 | export("keys<-") 10 | export("values<-") 11 | export(attach_hash) 12 | export(clear) 13 | export(copy) 14 | export(del) 15 | export(delete) 16 | export(detach_hash) 17 | export(has.key) 18 | export(has_key) 19 | export(hash) 20 | export(invert) 21 | export(inverted.hash) 22 | export(is.empty) 23 | export(is.hash) 24 | export(keys) 25 | export(kv) 26 | export(setkeys) 27 | export(values) 28 | exportClasses(hash) 29 | exportMethods("values<-") 30 | exportMethods(clear) 31 | exportMethods(names) 32 | import(methods) 33 | import(utils) 34 | importFrom(methods,is) 35 | importFrom(methods,new) 36 | importFrom(utils,head) 37 | importFrom(utils,tail) 38 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # 2019-01-04 Version 3.3.0 2 | 3 | * Converted documentation to MD via `roxygen2md()` 4 | 5 | * Added additional tests 6 | 7 | * `values()` now uses `mget`. Thanks to @MRohani for leading to this find. 8 | 9 | * `make.keys()` -> `make_keys()`; `make.keys()` will be deprecated in the future. 10 | 11 | * `has.key()` -> `has_key()`; `has.key()` will be deprecated in the future. 12 | 13 | * Added `head()` and `tail()` methods. 14 | 15 | * Added `as.data.frame.hash()` method. 16 | 17 | # 2017-03-01 Vesion 3.2.0 18 | 19 | * added `kv` function to iterate by key and value 20 | 21 | * `values` always returns a named list. Use `unlist` to simplify 22 | (#13: Florent Angly) 23 | 24 | * hash operation no longer sorts by key, providing an speed-up especially for 25 | large hashes (#15: Florent Angly) 26 | 27 | * `names` has been removed. Now calling names uses `base::names` by default, 28 | `keys` should be mostly used instead. It is faster and more flexible. 29 | 30 | * Hash key misses with `[` returns `NULL` if not found. (#1: Rohani) 31 | 32 | * `values` now will always return a list. Use `unlist` to create a vector. 33 | 34 | # 2014-11-20 Version 3.0.2 35 | 36 | * improve and expand tests especially of all `hash` constructor methods 37 | * improve examples in hash.R 38 | 39 | 40 | # 2014-11-20 Version 3.0.1 41 | 42 | * Move to new testthat standard 43 | * Fix imports of utils and method packages 44 | * Documentation fixes 45 | 46 | # 2014-02-01 Version 3.0.0 47 | 48 | ## Incompatibe Changes breaking some backward compatability: 49 | 50 | * `h[]` returns a copy of h 51 | * .set is nolonger supported. Standard R accessors should be used. 52 | 53 | ## New features 54 | 55 | * New ways to edit key names: 56 | ** `setkeys` allows setting of key names similar to data.table's `setnames` 57 | ** `keys<-` for setting simple keys 58 | 59 | * `names` works same as `keys` 60 | 61 | 62 | ## Organizational Changes 63 | 64 | * Fully converted to roxygen2 documentation 65 | * Better organized files 66 | * much improved documetation. 67 | 68 | 69 | # OlDER CHANGES 70 | 71 | 72 | 2013-02-20 Version 2.2.6 73 | - Fixes bug with '[' assessor which was broken. Thanks to Jeremy Rassen. 74 | - Adds some test for accessors using testthat 75 | 76 | 2013-01-31 Version 2.2.5 77 | Fixes bug with has.key. Because of the default settingg of inherits=TRUE in 78 | the exists function, the has.key function was revealing keys for objects 79 | found on inherited environments, this included functions such as 'c' and 'q'. 80 | Thanks to Michael Pratt for spotting the bug. 81 | 82 | 2012-04-25 Version 2.2.0 83 | Recaptured orphaned package 84 | - R/zzz.R: uses utlis::packageVersion(pkgname, libname) 85 | - R/DESCRIPTION: Dependency to R-2.12.0+ 86 | 87 | 2011-03-17 Version 2.1.0 88 | Changed contact information. 89 | 90 | 2010-09-26 Version 2.0.2 (cbrown) 91 | Just releasing as version 2.0.2. Mostly, documentation and benchmarks added. 92 | Unreleased to CRAN. 93 | 94 | 2010-07-25 95 | - demo/hash-benchmarks.R has been expanded. 96 | 97 | 2010-06-14 98 | - man/hash-package.Rd 99 | - man/hash-class.Rd: Ammended notes section specifically being more 100 | clear about the PASS-BY-REFERENCE BEHAVIOR of HASHES 101 | 102 | 2010-05-26 Version 2.0.1 (cbrown) 103 | - At the request of Michael Hahsler, removed the ODG ASCII ART logo in the 104 | .OnLoad method. It will comeback as soon as I can figure out how to disable 105 | it through the appropriate option. 106 | 107 | Hi Christopher, 108 | 109 | thank you for providing the package hash. I am thinking of using 110 | it in my rEMM package. Is there a way to make the load message a 111 | little less flashy. I include hash in my DESCRIPTION file and the 112 | ASCII art comes always up when my package loads. 113 | 114 | Thanks, 115 | Michael 116 | 117 | -- Dr. Michael Hahsler, Visiting Assistant Professor 118 | Department of Computer Science and Engineering 119 | Lyle School of Engineering 120 | Southern Methodist University, Dallas, Texas 121 | 122 | 2010-04-24 Version 2.0.0 (cbrown) 123 | 124 | - The coercion of keys make.keys has largely been deprecated. This might change 125 | in future version, but what we really want is to have any object stand for the 126 | keys that will get automatically converted. This might be make.key in the 127 | future. Also, we removed .get. All the accessor coding now exists in the 128 | definition of the native accessors. 129 | 130 | - R/format : implement max.print to display only getOption('max.print') keys. 131 | 132 | - R/na.actions : 133 | na.*.hash function's have been renamed to hash.na.function. This is so as not to 134 | conflict with the base::na.* S3 functions. It is unfortunate, because it would 135 | have been nice to retain a name. It was impossible because of the generic form 136 | of the functions, na.fail which requires an object as the first argument. Meanwhile, 137 | the 'ifnotfound' argument of mget takes one argument. mget is extensively used 138 | in this package and the one argument that needs to be passed is the name of the 139 | key. The choice was to either re-write the standardGeneric which will cause 140 | downstream problems -OR- abandone the na.* names. The latter was chosen with 141 | the added benefit that other hash controlled options would be grouped by hash.* 142 | in the option vector. 143 | 144 | - R/Class-hash: 145 | - [[, [[<-: DEPRECATED 146 | + methods deprecated because new objects can inherit from environments. 147 | - $ : DEPRECATED 148 | + methods deprecated because new objects can inherit from environments 149 | + NB. $<- is still retained so that ha$a <- NULL will remove 'a' from ha. 150 | - [: 151 | + No longer relise on .set, creates new hash directly 152 | 153 | - R/hash-benchmark.R 154 | + Uses rbenchmark to check various perfomance metrics 155 | 156 | - DESCRIPTION: 157 | + Suggests: rbenchmark (>= 0.3) 158 | 159 | - R/get-R: .get DEPRECATED for perfomance reasons. 160 | 161 | - R/values.R: 162 | + values() 163 | - redefined as function(x, keys=NULL, ... ) 164 | - no longer uses .get 165 | 166 | - R/hash-action.R : DEPRECATED 167 | After renaming these, it was decided that these would be DEPRECATED. 168 | Sometimes consitency is better than customizabilty. 169 | replaces R/na.action with the following funcitons renamed: 170 | + hash.na.fail => hash.fail 171 | + hash.na.warn => hash.warn 172 | + hash.na.default => hash.default 173 | 174 | 175 | 176 | 177 | 2010-03-15 178 | - Passes Checks on R 2.9.2, 2.10.1. 2.11.0 (devel) 179 | - Warning on CHECK: 180 | Defining type "environment" as a superclass via class ".environment" 181 | 182 | Some R data types do not behave normally, in the sense that they are non-local references 183 | or other objects that are not duplicated. Examples include those corresponding to classes 184 | "environment", "externalptr", and "name". These can not be the types for objects with 185 | user-defined classes (either S4 or S3) because setting an attribute overwrites the object 186 | in all contexts. It is possible to define a class that inherits from such types, through 187 | an indirect mechanism that stores the inherited object in a reserved slot. The 188 | implementation tries to make such classes behave as if the object had a data part of the 189 | corresponding object type. Methods defined with the object type in the signature should 190 | work as should core code that coerces an object to the type in an internal or primitive 191 | calculation. There is no guarantee, however, because C-level code may switch directly on 192 | the object type, which in this case will be "S4". The cautious mechanism is to use 193 | as(x, "environment") or something similar before doing the low-level computation. See 194 | the example for class "stampedEnv" below. 195 | 196 | - R/Class-hash.R: Added if( getRversion ) to accomodate older and newer versions of R. 197 | - man/hash-accessors.Rd: added alias for $-hash,NULL-method. 198 | 199 | 200 | 2010-02-16 Version 1.99.3 (cbrown) 201 | - Fixed several typos 202 | - R/Class-hash.R 203 | + [[ now allows for na.action and works correctly 204 | + $ now calls [[ rather than get 205 | 206 | 2010-02-16 Version 1.99.1 (cbrown) 207 | - Fixed S4 Documentation Bugs throughtout 208 | - R/Class-hash.R : sped up $ and [[ with 'try' was previously using keys which is 209 | very slow by comparison. 210 | - R/values.R : fixed definition to values<- 211 | 212 | 2010-02-16 Version 1.99.0 (cbrown) 213 | RELEASE CANDIDATE FOR VERSION 2.00 214 | THIS VERSION BREAKS BACKWARD COMPATABILITY WHEN TRYING TO ACCESS A 215 | NON-EXISTANT KEY. PACKAGE NOW RETURNS NA BY DEFAULT, BUT BAHAVIOUR IS 216 | CONTROLABLE BY Options('na.default.hash') 217 | 218 | - R/Class-hash.R: fixed $ accessor to remove 'name' 219 | - R/get: 220 | - Added ability to control the default action when non-existant 221 | keys are requested. Thanks Matthias Buch-Kromann. 222 | - Deault for non-existant keys is NA. 223 | - Added customizable behavior for accessing non-existing keys 224 | - man/hash-pacakge.Rd: Added note comparing hash implementation to 225 | native environments 226 | 227 | 2010-02-15 Version 1.10.3 (cbrown) 228 | - R/keys.R: added all.names = T to show even hidden names. 229 | - R/values.R: 230 | + Added keys argument to 'values' and 'values<'- methods. 231 | + 'values' Passes ... argument to .get method 232 | Thanks Matthias Buch-Kromann. 233 | 234 | 2010-01-01 Version 1.10.2 (cbrown) 235 | - R/Class-hash.R 236 | + Added methods signature [-hash,missing,... to return the case when 237 | no indexes are provided to the hash slice method. 238 | - R/get.R 239 | + get.R will not return a simplified version from sapply call. When the 240 | hash had values with all the same elements, a matrix was returned and 241 | this interferred with the hash slices, [. I am not sure the behavior 242 | was even useful. 243 | - R/values.R 244 | + Added replacement method for values 245 | - R/zzz.R 246 | + Make it so that the odg.logo is displayed only once per session. 247 | 248 | 2009-12-09 Version 1.10.1 (cbrown) 249 | - R/Class-hash.R 250 | + Removed 'name' from signature for methods: $, $<- 251 | 252 | 2009-11-29 Version 1.10.0 (cbrown) 253 | - R/set.R 254 | + Fixed problem pointed out by Denise Maudlin from blog.opendatagroup.com 255 | key <- 'one' 256 | ikey <- 'two' 257 | val <- 'three' 258 | info <- hash() 259 | info[key] <- hash( keys=c(ikey), values=c(val) ) 260 | Error in get(make.keys(i), x@.Data) : object ‘1′ not found 261 | Solution is to check if only one key is provided than the values are 262 | are the value vector. 263 | - tests/set.r 264 | + Added test for adding hashes as values. 265 | - Class-hash.R 266 | + [[-method: verifies if argument is a previously assigned key. If not, 267 | method returns NULL with a warning. 268 | NULL with a warning. 269 | + $-method: verifies if argument is a previously assigned key. If not, 270 | method returns NULL with a warning. 271 | 272 | 2009-11-11 (cbrown) 273 | - R/zzz.R 274 | + Fixed logo 275 | - R/invert.R 276 | + Made better generic for use with formula tools 277 | 278 | 2009-11-04 Version 1.0.3 (cbrown) 279 | + Fixed dependency of R-2.9.0 280 | 281 | 2009-10-14 (cbrown) 282 | + show.R 283 | - Handled cases where values are not supported by "format". These are collapsed as character 284 | 285 | 2009-10-11 (cbrown) Version 1.0.2 286 | + revert previous change allowing [[(hash) to accept multiple keys 287 | 288 | 289 | 2009-10-09 (cbrown) Version 1.0.1 ( not released on CRAN ) 290 | + [[(hash) 291 | support for multiple supplied keys also passes ... to simplify 292 | + now properly inherits from environment 293 | + requires R>=2.9.0 294 | 295 | 2009-09-30 (cbrown) 296 | + validate.key rename make.keys 297 | - This is more R-ish and more like the make.names function. 298 | 299 | 2009-09-28 (cbrown) 300 | + R/hash.R: 301 | - Fixed format of hash accessors. Now hashes can contain hashes. 302 | - Deprecated use of [[ with multipe keys. 303 | 304 | + R/show.R: Now aliases format 305 | + R/format.R: added 306 | + R/zzz.R: added graphical Open Data Logo 307 | + R/print.R: added 308 | 309 | 310 | 2009-09-04 (cbrown) 311 | - R/zzz.R: added Open Data message 312 | 313 | 2009-09-04 Version 0.40 (cbrown) 314 | - R/get.R: Added drop to reduce to lowest dimension by default. 315 | + R/invert.R: 316 | + invert method added. 317 | + inverted.hash function added. 318 | 319 | 320 | -------------------------------------------------------------------------------- /R/Class-hash.R: -------------------------------------------------------------------------------- 1 | #' Class "hash" 2 | #' 3 | #' Implements a S4 hash class in R similar to hashes / associatesd arrays / 4 | #' dictionaries in other programming languages. Where possible, the hash class 5 | #' uses the standard R accessors: `\$`, `[` and `[[` as well as 6 | #' other common hash methods. Hash construction is flexible and takes several 7 | #' syntaxes. See the constructor method [hash()] for detaiols 8 | #' 9 | #' For shorter key-value pairs, lists yield higher performance. Hashes 10 | #' outperform list once the have an appreciable length typically greater than 11 | #' 100 elements. Hash objects have defined methods that (should) make them 12 | #' flexible, intuitive and easy to use especially for developers familar with 13 | #' other languages. 14 | #' 15 | #' The `haah` class inherits from environment and has no defined slots. It is 16 | #' essentially a wrapper around [base::environment()] and is very similar to 17 | #' reference classes in that most of the semantic are pass-by-reference rather 18 | #' than pass-by-value. For this reason, some of the behaviour is not as expected. 19 | #' 20 | #' @slot .xData an [base::environment()] 21 | #' 22 | #' @author Christopher Brown 23 | #' 24 | #' @seealso 25 | #' - [extract] : for accessor methods 26 | #' - [is.hash()] : for testing whether the object is a hash 27 | #' - [as.list.hash()] : for converting hash to a list 28 | #' 29 | #' @examples 30 | #' h <- new( "hash" ) # new empty hash 31 | #' h <- hash() # same 32 | #' class(h) # "hash" 33 | #' is.hash(h) # TRUE 34 | #' 35 | #' h[ letters ] <- 1:26 # populate the hash 36 | #' 37 | #' as.list( h ) # convert to a list 38 | #' 39 | #' hash( list(a=1,b=1) ) 40 | #' showClass("hash") 41 | #' 42 | #' @name hash-class 43 | #' @rdname hash-class 44 | #' @exportClass hash 45 | 46 | setClass( 'hash', contains = 'environment' ) 47 | -------------------------------------------------------------------------------- /R/as.data.frame.hash.R: -------------------------------------------------------------------------------- 1 | #' Turn a hash into a data.frame 2 | #' 3 | #' Transform a hash into a nrowx2 data.frame 4 | #' 5 | #' @param x hash 6 | #' @param key string; name for the column of keys (Default: "key"). 7 | #' @param value string; name for the column of values (Default: "value") 8 | #' @param ... additional arguments to [values()] 9 | #' 10 | #' @return 11 | #' a data.frame with as many row as elements in `x` and two columns one for the 12 | #' keys (always character) and value a list column 13 | #' 14 | #' 15 | #' @examples 16 | #' 17 | #' h <- hash( a=1, b=1:2, c=1:3) 18 | #' as.data.frame(h) 19 | #' 20 | #' @export 21 | 22 | as.data.frame.hash <- function(x, ..., key="key", value="value" ) { 23 | df <- as.data.frame( list( keys(x) ), col.names=key ) 24 | df[[value]] <- values(x) 25 | df 26 | } 27 | 28 | -------------------------------------------------------------------------------- /R/attach_hash.R: -------------------------------------------------------------------------------- 1 | #' attach/detach a hash to the search path 2 | #' 3 | #' @param hash,what hash object to attach to the search path. 4 | #' @param name string; name for the environment on the search path 5 | # @param pos integer where to attach the hash 6 | #' @param ... additional arguments passed to [base::attach()] 7 | #' 8 | #' `attach_hash` attaches the environment part of a hash to the search path. 9 | #' The values become directly accessible by their keys. 10 | #' 11 | #' `hash:::attach()` is a non-exported versions of the same. 12 | #' 13 | #' @seealso 14 | #' - [base::attach()] 15 | #' 16 | #' @examples 17 | #' h <- hash( a=1, b=2, c=3 ) 18 | #' 19 | #' attach_hash(h) 20 | #' search() 21 | #' detach_hash(h) 22 | #' search() 23 | #' 24 | #' hash:::attach( h ) 25 | #' b # 2 26 | #' b <<- 25 27 | #' b # 25 28 | #' h 29 | #' detach(h) 30 | #' 31 | #' @export 32 | 33 | attach_hash <- function(hash, name=deparse(substitute(hash)), ...) { 34 | base::attach( hash@.xData, name=name, ... ) 35 | } 36 | 37 | #' @rdname attach_hash 38 | #' @export 39 | 40 | detach_hash <- function(...) base::detach(...) 41 | 42 | 43 | #' @rdname attach_hash 44 | attach <- function( what, name=deparse(substitute(what)), ... ) { 45 | base::attach( what@.xData, name = name, ... ) 46 | } 47 | -------------------------------------------------------------------------------- /R/clear.R: -------------------------------------------------------------------------------- 1 | #' Removes all key-value pairs from a hash 2 | #' 3 | #' `clear` removes all key-values from a hash. 4 | #' 5 | #' @param x A `hash` object. 6 | #' 7 | #' @details 8 | #' 9 | #' `clear` removes (`rm`) the key-value pairs on the hash. 10 | #' For large hashes it might be faster to reinitialize the hash, though this 11 | #' might cause memory leaks. 12 | #' 13 | #' @note `clear` should be called prior to removing a hash. This ensures 14 | #' that the memory from the environment is freed. 15 | #' 16 | #' @return None. Method clear exists entirely for its side effects. 17 | #' 18 | #' @author Christopher Brown 19 | #' 20 | #' @seealso 21 | #' - [del()] to remove specific key-values from the hash. 22 | #' - [hash()] to create a new hash 23 | #' 24 | #' @examples 25 | #' 26 | #' h <- hash( letters, 1:26 ) 27 | #' h # An object of type 'hash' containing 26 key-value pairs. 28 | #' clear(h) 29 | #' h # An object of type 'hash' containing 0 key-value pairs. 30 | #' 31 | #' @rdname clear 32 | #' @export 33 | 34 | setGeneric( "clear", function(x) standardGeneric("clear") ) 35 | 36 | #' @export 37 | #' @rdname clear 38 | 39 | setMethod( "clear" , "hash" , 40 | function(x) rm( list=keys(x), envir=x@.Data ) 41 | ) 42 | -------------------------------------------------------------------------------- /R/copy.R: -------------------------------------------------------------------------------- 1 | #' copy 2 | #' 3 | #' Create a *copy* of a hash. 4 | #' 5 | #' @param x hash 6 | #' @param ... additional arguments 7 | #' 8 | #' Creatinga copy using the assingment operator, `<-`, does not work as 9 | #' expected, since hashes are based on environments and environments are 10 | #' reference objects in R. The assignment operator consequently creates a 11 | #' linked copy the original hash and not an independent copy. The `copy` 12 | #' method provides an identical unlinked copy of the hash. 13 | #' 14 | #' @return hash, copy of hash `x` 15 | #' 16 | #' @author Christopher Brown 17 | #' 18 | #' @seealso 19 | #' [base::environment()] 20 | #' 21 | #' @examples 22 | #' h <- hash( a=1, b=2 ) 23 | #' g <- h 24 | #' g$a <- "foo" 25 | #' h$a # "foo" 26 | #' 27 | #' h_new <- copy( h ) 28 | #' h_new$a <- "bar" 29 | #' h$a # still "foo" 30 | #' 31 | #' @name copy 32 | #' @rdname copy 33 | #' @docType methods 34 | #' @export 35 | 36 | setGeneric( 'copy', function(x,...) standardGeneric( 'copy' ) ) 37 | 38 | 39 | #' @name copy,hash-method 40 | #' @rdname copy 41 | 42 | setMethod( 'copy', 'hash', 43 | function(x, ... ) { 44 | len <- length(x) 45 | if( len == 0 ) { 46 | hash() 47 | } else { 48 | hash( mget( keys(x), x@.Data ) ) 49 | } 50 | } 51 | ) 52 | -------------------------------------------------------------------------------- /R/del.R: -------------------------------------------------------------------------------- 1 | #' Remove key-value pair(s) from a hash 2 | #' 3 | #' Removes key-value pair(s) from a hash by name of the object. There are also 4 | #' R-like methods described in [Extract]. To delete all keys, use 5 | #' [clear()]. 6 | #' 7 | #' @param x An object that will be coerced to valid key(s) to be removed from 8 | #' the hash. `x` will be coerced to a valid hash keys using 9 | #' [make_keys()] 10 | #' @param hash A [hash()] object 11 | #' @return None. This method exists solely for the side-effects of removing 12 | #' items from the hash. 13 | #' 14 | #' @author Christopher Brown 15 | #' 16 | #' @seealso 17 | #' - [base::rm()] base function used by `del` 18 | #' - [Extract] for R-like accessor 19 | #' - [clear()] to remove all key-values and return an empty hash 20 | #' - [hash()] 21 | #' 22 | #' @keywords methods data manip 23 | #' @examples 24 | #' 25 | #' h <- hash( letters, 1:26 ) 26 | #' 27 | #' # USING del 28 | #' del( "a", h ) # delete key a 29 | #' del( c("b","c"), h ) # delete keys b, c 30 | #' 31 | #' # USING rm 32 | #' rm( "d", envir=h ) # delete key d 33 | #' rm( list= c("e","f"), envir=h ) # delete keys e,f 34 | #' 35 | #' # USING R syntsx 36 | #' h$g <- NULL # delete key g 37 | #' h[['h']] <- NULL # delete key h 38 | #' h['i'] <- NULL # delete key i 39 | #' h[ c('j','k')] <- NULL # delete keys e,f 40 | #' 41 | #' keys(h) 42 | #' D 43 | #' 44 | #' @name del 45 | #' @aliases delete del-methods delete-methods 46 | #' @rdname del 47 | #' @docType methods 48 | #' @import methods 49 | #' @export 50 | 51 | setGeneric( "del", function( x, hash ) { standardGeneric("del") } ) 52 | 53 | 54 | #' @name del,ANY,hash-method 55 | #' @rdname del 56 | 57 | setMethod( 58 | "del" , c( "ANY", "hash" ) , 59 | function ( x, hash ) 60 | rm( list=make_keys(x), envir=hash@.Data ) 61 | ) 62 | 63 | 64 | #' @name del,character,hash-method 65 | #' @rdname del 66 | 67 | setMethod( "del", c( 'character', 'hash' ), 68 | function( x, hash ) 69 | rm( list=x, envir=hash@.Data) 70 | ) 71 | 72 | 73 | #' @name delete 74 | #' @aliases delete 75 | #' @rdname del 76 | #' @export 77 | 78 | setGeneric( "delete", function( x, hash ) { standardGeneric("delete") } ) 79 | 80 | 81 | #' @rdname del 82 | #' @aliases delete,ANY,hash-method 83 | #' @import methods 84 | 85 | setMethod( 86 | "delete", 87 | methods::signature( "ANY", "hash" ) , 88 | function(x,hash) { del(x,hash) } 89 | ) 90 | -------------------------------------------------------------------------------- /R/extract.R: -------------------------------------------------------------------------------- 1 | #' Extract 2 | #' 3 | #' These are the hash accessor methods. They closely follow the R style. 4 | #' 5 | #' 6 | #' @param x [hash()] object 7 | #' @param i keys to get or set 8 | #' @param j unused; retained to be compatoble with base package 9 | #' @param drop unused; retained to be compatible with base package 10 | # @param keys a vector of keys to be returned. 11 | # @param value For the replacement method, the value(s) to be set. 12 | #' @param ... Arguments passed to additional methods [sapply()] 13 | #' @param value the value to set for the key-value pair 14 | #' @param name the key name 15 | #' 16 | #' `$` is a look-up operator for a single key. The base `$` method 17 | #' are used directly on the inherited environment. The supplied key is taken 18 | #' as a string literal and is not interpreted. The replaement form, `$<-` 19 | #' mutates the hash in place. 20 | #' 21 | #' `[[` is the look-up, extraction operator. It returns the value of a 22 | #' single key and will interpret its argument. The replacement method, 23 | #' `[[<-` mutates the hash in place. 24 | #' 25 | #' `[` is a slice operator. It returns a hash with the subset of key-value 26 | #' pairs. Unlike the other accessor methods, `[` returns a *copy*. 27 | #' 28 | #' All hash key misses return `NULL`. All hash key replacements with NULL 29 | #' delete the key-value pair from the hash. 30 | #' 31 | 32 | #' `$` and `[[` return the value for the supplied argument. If 33 | #' `i` is not a key of `x`, `NULL` is returned with a warning. 34 | #' 35 | #' `[` returns a hash slice, a subhash copy `x` with only the keys 36 | #' `i` defined. 37 | #' 38 | #' See details above for the complete explanation. 39 | #' 40 | #' @author Christopher Brown 41 | #' 42 | #' @seealso 43 | #' 44 | #' - [del()] for removing keys 45 | #' - [clear()] for removing all keys 46 | #' - [keys()] to get/set/rename keys 47 | #' - [values()] to get/set/edit values 48 | # [set()] to set values internal method 49 | #' - [hash()] 50 | #' 51 | #' 52 | #' @examples 53 | #' 54 | #' h <- hash( c('a','b','c'), 1:3 ) 55 | #' 56 | #' # NAMED ACCESS 57 | #' 58 | #' h$a # 1 59 | #' h$c # 3 60 | #' 61 | #' # class of values change automatically 62 | #' class(h$a) # integer 63 | #' h$a <- 1.1 64 | #' class(h$a) # numeric 65 | #' 66 | #' # values can contain more complex objects 67 | #' h$a <- 1:6 68 | #' h 69 | #' 70 | #' h$a <- NULL # DELETE key 'a', will return null 71 | #' 72 | #' 73 | #' # INTERPRETED ACCESS 74 | #' 75 | #' h[[ "a" ]] <-"foo" # Assigns letters, a vector to "foo" 76 | #' nm = "a" 77 | #' 78 | #' # SLICE ACCESS 79 | #' h[ nm ] <- "bar" # h$a == bar 80 | #' h[ nm ] <- NULL 81 | #' 82 | #' 83 | #' # Slice 84 | #' h[ keys(h) ] 85 | #' h[ keys(h) ] <- list( 1:2, 1:3 ) 86 | #' h 87 | #' 88 | #' @name extract 89 | #' @rdname extract 90 | #' @docType methods 91 | #' @aliases extract 92 | 93 | NULL 94 | 95 | 96 | ## --------------------------------------------------------------------- 97 | ## [ : SLICE METHOD 98 | ## --------------------------------------------------------------------- 99 | 100 | #' @name [,hash,ANY,missing,missing-method 101 | #' @rdname extract 102 | 103 | setMethod( 104 | '[' , 105 | signature( x="hash", i="ANY", j="missing", drop = "missing") , 106 | function( 107 | x,i,j, ... , 108 | # na.action = 109 | # if( is.null( getOption('hash.na.action') ) ) NULL else 110 | # getOption('hash.na.action') , 111 | drop 112 | ) { 113 | 114 | .h <- hash() # Will be the new hash 115 | for( k in i ) 116 | assign( k, mget( x=k, envir=x@.xData, ifnotfound = list(NULL) ), .h@.Data ) 117 | 118 | .h 119 | 120 | } 121 | ) 122 | 123 | 124 | #' @name [,hash,missing,missing,missing-method 125 | #' @rdname extract 126 | 127 | setMethod( '[', signature( 'hash', 'missing', 'missing', 'missing' ), 128 | function(x,i,j, ..., drop ) { 129 | x 130 | } 131 | ) 132 | 133 | ## -------------------------------------------------------------------- 134 | ## [<- : SLICE REPLACEMENT 135 | ## --------------------------------------------------------------------- 136 | # NB. Although we would like to use assign directly, we use set 137 | # because it deals with the ambiguity of the lengths of the 138 | # key and value vectors. 139 | 140 | 141 | #' @name [<-,hash,ANY,missing,ANY-method 142 | #' @rdname extract 143 | 144 | setReplaceMethod( '[', c(x ="hash", i="ANY" ,j="missing", value="ANY") , 145 | function( x, i, ..., value ) { 146 | .set( x, i, value, ... ) 147 | x 148 | } 149 | ) 150 | 151 | 152 | 153 | #' @name [<-,hash,ANY,missing,NULL-method 154 | #' @rdname extract 155 | 156 | setReplaceMethod( '[', c(x="hash", i="ANY", j="missing", value="NULL") , 157 | function( x, i, ..., value ) { 158 | del( i, x ) 159 | x 160 | } 161 | ) 162 | 163 | 164 | # TEST: 165 | # h[ "foo" ] <- letters # Assigns letters, a vector to "foo" 166 | # h[ letters ] <- 1:26 167 | # h[ keys ] <- value 168 | # h[ 'a' ] <- NULL 169 | 170 | 171 | 172 | ## -------------------------------------------------------------------- 173 | ## $ - named accessor 174 | ## 175 | ## $ -- DEPRECATED 176 | ## This is deprecated since '$' is defined on environments and 177 | ## environments can be inherited in objects. There is not need for a 178 | ## special functions 179 | # --------------------------------------------------------------------- 180 | 181 | ## -------------------------------------------------------------------- 182 | ## $<- - named replacement 183 | ## -------------------------------------------------------------------- 184 | # SPECIAL CASE: NULL value 185 | # When assign a null values to a hash the key is deleted. It is 186 | # idiomatic when setting a value to NULL in R that that value is 187 | # removed from a list or environment. 188 | # 189 | # If R's behavior changes this will go away. 190 | # It is interesting to note that [[ behaves this way 191 | 192 | 193 | #' @name $<-,hash,NULL-method 194 | #' @rdname extract 195 | 196 | setReplaceMethod( '$', c( x="hash", value="NULL"), 197 | function(x, name, value) { 198 | remove( list=name, envir=x@.Data ) 199 | x 200 | } 201 | ) 202 | 203 | 204 | ## --------------------------------------------------------------------- 205 | ## [[ -- interpret accessor (DEPRECATED) 206 | ## 207 | ## This is deprecated since this is handled by R natively. 208 | ## Return single value, key,i, is a name/gets interpretted. 209 | ## 210 | ## --------------------------------------------------------------------- 211 | 212 | ## --------------------------------------------------------------------- 213 | ## [[ -- interpreted replacement 214 | ## --------------------------------------------------------------------- 215 | 216 | #' @name [[<-,hash,ANY,missing,ANY-method 217 | #' @rdname extract 218 | 219 | setReplaceMethod( '[[', c(x="hash", i="ANY", j="missing", value="ANY") , 220 | function(x,i,value) { 221 | assign( i, value, x@.Data ) 222 | x 223 | } 224 | ) 225 | 226 | 227 | #' @name [[<-,hash,ANY,missing,NULL-method 228 | #' @rdname extract 229 | 230 | setReplaceMethod( '[[', c(x="hash", i="ANY", j="missing", value="NULL") , 231 | function(x,i,value) { 232 | rm( list=i, envir=x@.Data ) 233 | x 234 | } 235 | ) 236 | 237 | 238 | -------------------------------------------------------------------------------- /R/format.R: -------------------------------------------------------------------------------- 1 | #' format - format a hash object for print 2 | #' 3 | #' Format a hash for printing/showing. 4 | #' 5 | #' @param x hash object 6 | #' @param max.print maximum numbers of rows to print 7 | #' @param ... additional arguments 8 | #' 9 | #' @seealso 10 | #' [format()] 11 | #' 12 | #' @name format 13 | #' @aliases format,hash-method format 14 | #' @docType methods 15 | 16 | setMethod( "format", "hash", 17 | 18 | function( x, max.print = getOption('max.print'), ... ) { 19 | 20 | indent <- list(...)$indent 21 | 22 | if( is.null(indent) ) { 23 | indent <- "" 24 | } else { 25 | indent <- paste( indent, " ", sep="" ) 26 | } 27 | 28 | indent2 <- paste( indent, " ", sep="" ) 29 | 30 | ret <- paste( " containing ", length(x), " key-value pair(s).\n", sep="" ) 31 | 32 | i <- 0 33 | for ( k in keys(x)[1:min(length(x),max.print)] ) { 34 | 35 | # vals <- paste( format( x[[k]], indent=indent ), collapse = " " ) 36 | # THERE ARE SOME CASES WHERE FORMAT DOESN'T WORK, WE TRAP THESE. 37 | 38 | vals <- try( paste( format( x[[k]], indent=indent ), collapse = " " ), silent=T ) 39 | if( inherits( vals, "try-error" ) ) vals <- paste( as.character( x[[k]] ), collapse=", " ) 40 | 41 | ret <- paste( ret, indent2, k, " : ", vals, "\n", sep="" ) 42 | 43 | i <- i + 1 44 | if( i >= max.print ) { 45 | ret <- paste( ret, "Reached getOption(max.print)=", max.print, " -- omitted ", 46 | length(x) - max.print, " entries.\n", sep="" 47 | ) 48 | break 49 | } 50 | 51 | } 52 | 53 | ret <- gsub( "\n\n", "\n", ret ) 54 | ret 55 | 56 | } 57 | ) 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /R/has_key.R: -------------------------------------------------------------------------------- 1 | #' Test for existence of key(s) on a hash 2 | #' 3 | #' **Note:** `has.key()` will be replaced by `has_key()` in the future. 4 | #' ` 5 | #' @param hash **hash** object. 6 | #' @param key vector whose entries will be coerced to valid keys. 7 | #' @param ... arguments passed to sapply. 8 | #' 9 | #' @details 10 | #' 11 | #' `has_key` indicates whether the key exists on the hash. 12 | #' 13 | #' `has_key` is a wrapper around [base::exists()] vectorized over `key` using 14 | #' [base::sapply()]. Additional arguments, `...` are passed to [base::sapply()]. 15 | #' The inherits method is always `FALSE` 16 | #' 17 | #' `has_key()` is replacing `has.key` and switching the order of arguments. This 18 | #' makes `has_key()` more pipe-able and readable. `has.key()` will be deprecated 19 | #' in a future release. 20 | #' 21 | #' @return logical vector with `length(key)`` indicating whether 22 | #' the key is defined in the hash. 23 | #' 24 | #' @seealso 25 | #' - [base::exists()] 26 | #' 27 | #' @examples 28 | #' h <- hash( letters, 1:26 ) 29 | #' all( has.key( letters, h ) ) # TRUE 30 | #' 31 | #' @export 32 | 33 | has_key <- function(hash, key, ...) UseMethod('has_key') 34 | 35 | #' @export 36 | has_key.hash <- function(hash,key, ...) { 37 | sapply( make_keys(key), exists, hash@.Data, inherits=FALSE, ... ) 38 | } 39 | 40 | 41 | # @name has.key 42 | #' @rdname has_key 43 | #' @docType methods 44 | # @aliases has.key 45 | #' @import methods 46 | #' @export 47 | 48 | setGeneric( 49 | "has.key", 50 | function( key, hash, ... ) standardGeneric( "has.key" ) 51 | ) 52 | 53 | 54 | #' @rdname has_key 55 | #' @aliases has.key,ANY,hash-method 56 | 57 | setMethod( 58 | "has.key" , 59 | methods::signature( "ANY", "hash" ) , 60 | function( key, hash, ... ) { 61 | # .Deprecated("has_key" 62 | # , msg=paste0( sep=" " 63 | # , "has.key() will be deprecated in the future in favor of has_key().\n" 64 | # , "has_key() changes the order of arguments accepting the hash " 65 | # , "first in order to make has_key pipable." ) 66 | # ) 67 | sapply( key, exists, hash@.Data, inherits=FALSE ) 68 | } 69 | ) 70 | -------------------------------------------------------------------------------- /R/hash-package.R: -------------------------------------------------------------------------------- 1 | #' hash package 2 | #' 3 | #' Hash/associative array/dictionary data structure for the R language. 4 | #' 5 | #' This S4 class is designed to provide a hash-like data structure in a native 6 | #' R style and provides the general methods for hash/dictionary operations. 7 | #' 8 | #' 9 | #' @note 10 | #' 11 | #' The hash package is the only full featured hash implementation for the R 12 | #' language. It provides more features and finer control of the hash behavior 13 | #' than the native features. It hss similar and some cases better 14 | #' performance, e.g. compared to lists with a large number of elements. 15 | #' 16 | #' @author Christopher Brown 17 | #' 18 | #' Maintainer: Christopher Brown 19 | #' @seealso 20 | #' \code{\link{hash} }, 21 | #' [Extract()] and 22 | #' \code{\link{environment} } 23 | #' 24 | #' @references 25 | #' Some discussion can be found here: 26 | # \href{\url{http://www.mail-archive.com/r-help@r-project.org/msg37637.html}} 27 | # \href{\url{http://www.mail-archive.com/r-help@r-project.org/msg37650.html}} 28 | # \href{\url{http://tolstoy.newcastle.edu.au/R/help/05/12/index.html\#18192}} 29 | #' 30 | #' @examples 31 | #' 32 | #' h <- hash( keys=letters, values=1:26 ) 33 | #' h <- hash( letters, 1:26 ) 34 | #' 35 | #' h$a # 1 36 | #' 37 | #' h$foo <- "bar" 38 | #' h[ "foo" ] 39 | #' h[[ "foo" ]] 40 | #' 41 | #' clear(h) 42 | #' rm(h) 43 | #' 44 | #' @name hash-package 45 | #' @docType package 46 | #' @import methods 47 | #' @import utils 48 | 49 | NULL 50 | -------------------------------------------------------------------------------- /R/hash.R: -------------------------------------------------------------------------------- 1 | #' hash/associative array/dictionary data structure for the R language 2 | #' 3 | #' Functions for creating and working with hash objects: 4 | #' 5 | #' `hash` Class constructor 6 | #' 7 | #' `is.hash` test if object is of class "hash" 8 | #' 9 | #' `as.list` 10 | #' 11 | #' `as.list.hash` convert a hash object to a list 12 | #' 13 | #' @param x a hash object. 14 | #' 15 | #' @param all.names a logical indicating whether to copy all values or 16 | #' (default) only those whose names do not begin with a dot 17 | #' 18 | #' @param ... Additional arguments passed to the function 19 | #' #' HASH KEYS must be a valid character value and may not be the empty string 20 | #' `""`. 21 | #' 22 | #' 23 | #' @details 24 | #' 25 | #' *KEYS* must be a valid R name, must be a character vector and must not 26 | #' be the empty string, `""`. When supplied by the used methods will try to 27 | #' coerce the keys to valid names using [make_keys()] 28 | #' 29 | #' *VALUES* are restricted to any valid R objects. 30 | #' HASH VALUES can be any R value, vector or object. 31 | #' 32 | #' \emph{code{hash}} returns a hash object. Key-value pairs may be specified 33 | #' as: \cr 34 | #' * explicitly named arguments `keys` and `values` \cr 35 | #' * two unnamed objects of equal length as a set of key-value pairs \cr 36 | #' * key=value pairs \cr 37 | #' * a single naned vector \cr 38 | #' * as a list \cr 39 | #' 40 | # See \code{\link{.set}} for further details and how key-value vectors of 41 | # unequal length are interpretted. 42 | # 43 | #' *ACCESSORS.* Hashes may be accessed via the standard R accessors `[`, `[[` and 44 | #' `\$`. See [hash::extract()] for details. 45 | #' 46 | #' *PASS-BY REFERENCE.* Environments and hashes are special objects in R because 47 | #' only one copy exists globally. When provided as an argument to a function, no 48 | #' local copy is made. When passes to functions, those functions can change the 49 | #' value of the hash. This is not typical of R. 50 | #' 51 | #' *PERFORMANCE.* Hashes are based on R's native environments and are designed 52 | #' to be exceedingly fast using the environments internal hash table. For 53 | #' small data structures, a list will out-perform a hash in nearly every case. 54 | #' For larger data structure, i.e. > 500 key value pair the performance of the 55 | #' hash becomes faster. Much beyond that the performance of the hash far 56 | #' outperforms native lists. 57 | #' 58 | #' 59 | #' @return 60 | #' `hash` hash object 61 | #' 62 | #' `is.hash` logical value indicating if the argument is a hash. 63 | #' 64 | #' `as.list` list conversion from hash 65 | #' 66 | #' `length` integer; number of key-value pairs in the hash 67 | #' 68 | #' @author Christopher Brown 69 | #' 70 | #' @seealso 71 | #' [extract()] 72 | #' 73 | #' @examples 74 | #' 75 | #' hash() # empty 76 | #' h <- hash() # associate a name to the hash 77 | #' 78 | #' hash( new.env() ) # from environment 79 | #' 80 | #' hash( a=1, b=2, c=3 ) # key-value pairs using named arguments 81 | #' 82 | #' hash( letters[1:3], 1:3 ) # unamed args: two equal length vectors 83 | #' hash( letters[1:3], 1 ) # unamed args: all keys with same value 84 | #' 85 | #' hash( 1:3, lapply(1:3, seq, 1 )) # same, arbitrary values 86 | #' 87 | #' hash( c(a=1, b=2, c=3) ) # named vector of key-value pairs 88 | #' hash( list(a=1,b=2,c=3) ) # named list of key-value pairs 89 | #' 90 | #' is.hash( hash() ) 91 | #' as.list(h) # CONVERT TO LIST 92 | #' 93 | #' @rdname hash 94 | #' @aliases hash 95 | #' @importFrom methods new 96 | #' @export hash 97 | 98 | hash <- function( ... ) { 99 | 100 | li <- list(...) 101 | 102 | if( length(li) == 1 && is.environment( li[[1]] ) ) { 103 | h <- new( "hash", parent=emptyenv() ) 104 | h@.xData <- li[[1]] 105 | 106 | } else if( length(li) == 1 && is.list( li[[1]] ) ) { 107 | h <- hash( as.environment(li[[1]]) ) 108 | 109 | } else { 110 | h <- new( 111 | "hash" , 112 | new.env( hash = TRUE , parent=emptyenv() ) 113 | ) 114 | 115 | if ( length(li) > 0 ) { 116 | if( length(li) > 0 ) .set( h, ... ) 117 | } 118 | } 119 | 120 | h 121 | 122 | } 123 | 124 | 125 | # --------------------------------------------------------------------- 126 | # MISC. FUNCTIONS 127 | # --------------------------------------------------------------------- 128 | 129 | #' @aliases is.hash 130 | #' @rdname hash 131 | #' @importFrom methods is 132 | #' @export is.hash 133 | 134 | is.hash <- function(x) is( x, "hash" ) 135 | 136 | 137 | #' @aliases as.list.hash 138 | #' @rdname hash 139 | #' @export 140 | 141 | as.list.hash <- function(x, all.names=FALSE, ...) 142 | as.list( x@.Data, all.names, ... ) 143 | -------------------------------------------------------------------------------- /R/head.R: -------------------------------------------------------------------------------- 1 | #' Head or tail of a hash 2 | #' 3 | #' Returns the first or last part of a hash 4 | #' 5 | #' @param x hash; 6 | #' @param ... Additional arguments passed to [utils::head()] 7 | #' 8 | #' @description 9 | #' 10 | #' There is no guaranteed order to the keys-values are produced. This may change 11 | #' in the futures. 12 | #' 13 | #' @return 14 | #' A hash object display the first `n` key-value pairs. 15 | #' 16 | #' @seealso 17 | #' - [utils::head()] 18 | #' 19 | #' @examples 20 | #' 21 | #' h <- hash( letters, 1:26) 22 | #' head(h) 23 | #' tail(h) 24 | #' 25 | #' @importFrom utils head 26 | #' @importFrom utils tail 27 | #' @rdname head 28 | #' @export 29 | 30 | head.hash <- function(x, ...) 31 | x[ head( keys(x), ... ) ] 32 | 33 | #' @rdname head 34 | #' @export 35 | 36 | tail.hash <- function(x, ...) 37 | x[ tail( keys(x), ... ) ] 38 | -------------------------------------------------------------------------------- /R/invert.R: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # METHOD: invert( hash ) 3 | # produces a hash with the values as keys and the keys as values 4 | # ---------------------------------------------------------------------------------- 5 | 6 | #' Create/invert a hash. 7 | #' 8 | #' THIS IS AN EXPERIMENTAL FUNCTION. THE IMPLEMENTATION OR INTERFACE MAY CHANGE 9 | #' IN THE FUTURE. 10 | #' 11 | #' `invert` exchanges the keys and values of a hash. 12 | #' 13 | #' `inverted.hash` is a constructor method that directly makes an inverted 14 | #' hash. 15 | #' 16 | #' @param x A [hash()] object 17 | #' @param ... Arguments passed to the [hash()] function. 18 | #' 19 | #' @details 20 | #' Each element of the `values(x)` becomes a key in a new hash; the 21 | #' associatedis coerced to a key value is the 22 | #' The value becomes the associated key. 23 | #' 24 | #' For `inverted.hash`, a hash is created thnn inverted. It is defined 25 | #' as: 26 | #' 27 | #' ` function(...) invert(hash(...)) ` 28 | #' 29 | #' 30 | #' @return A hash object with: keys as the unique elements of `values(x)` 31 | #' and values as the associated \code{keys{x}} 32 | #' 33 | #' @author Christopher Brown 34 | #' 35 | #' @seealso See also \code{link{hash}} and [make_keys()] 36 | #' 37 | #' @examples 38 | #' 39 | #' h <- hash( a=1, b=1:2, c=1:3 ) 40 | #' invert(h) 41 | #' 42 | #' inverted.hash( a=1, b=1:2, c=1:3 ) 43 | #' 44 | #' @rdname invert 45 | #' @docType methods 46 | #' @export 47 | 48 | setGeneric( "invert", function(x) standardGeneric( "invert" ) ) 49 | 50 | 51 | #' @rdname invert 52 | 53 | setMethod( 'invert', 'hash', 54 | function(x) { 55 | h <- hash() 56 | for( k in keys(x) ) { 57 | for( v in make_keys(x[[k]]) ) { 58 | if ( ! has_key(h,v) ) h[[v]] <- k 59 | else h[[v]] <- append( h[[v]], k ) 60 | } 61 | } 62 | 63 | h 64 | } 65 | 66 | ) 67 | 68 | #' @rdname invert 69 | #' @export 70 | inverted.hash <- function(...) invert( hash(...) ) 71 | 72 | 73 | -------------------------------------------------------------------------------- /R/is.empty.R: -------------------------------------------------------------------------------- 1 | #' Test if a hash has no key-value pairs. 2 | #' 3 | #' `is.empty` tests to see if any key value pairs are assigned on a 4 | #' `hash` object. 5 | #' 6 | #' @param x hash object. 7 | #' 8 | #' Returns `TRUE` if no key-value pairs are defined for the hash, 9 | #' `FALSE` otherwise. 10 | #' 11 | #' @return logical. 12 | #' 13 | #' @author Christopher Brown 14 | #' 15 | #' @seealso 16 | #' [hash()] 17 | #' [exists()] 18 | #' [length()] 19 | #' 20 | #' @examples 21 | #' 22 | #' h <- hash( a=1, b=2, c=3 ) 23 | #' 24 | #' is.empty(h) # FALSE 25 | #' clear(h) 26 | #' is.empty(h) # TRUE 27 | #' h <- hash() 28 | #' is.empty(h) # TRUE 29 | #' 30 | #' @export 31 | 32 | is.empty <- function(x) { 33 | if( class(x) != 'hash' ) stop( "is.empty only works on hash objects" ) 34 | if( length(x) == 0 ) TRUE else FALSE 35 | } 36 | -------------------------------------------------------------------------------- /R/keys.R: -------------------------------------------------------------------------------- 1 | #' keys - get/set key(s) from a hash 2 | #' 3 | #' Returns the key(s) from a hash, unsorted by default 4 | #' 5 | #' @param x A [hash()] object. 6 | #' @param value character or object coercable to character 7 | #' @param sorted logical; whether the keys should be sorted 8 | #' (DEFAULT: `getOption('hash.sorted', FALSE)`) 9 | #' @param ... Used to allow for additional arguments for keys 10 | #' 11 | #' @details 12 | #' Returns the character vector containing the keys of a hash object. By 13 | #' default, the responses are not sorted. Set `sorted=TRUE` to return 14 | #' keys in sort order. 15 | #' 16 | #' `names` uses `base::names` and will always return a sorted list of 17 | #' names. `keys` should generally be used in favor of `names`, but 18 | #' names can be useful if you want sorted names for one instance when all others 19 | #' don't provide a sorted list. 20 | #' 21 | #' @return For `keys` and `names`, a character vector of key names 22 | #' For the replacement methods `keys<-`, a hash object with the keys 23 | #' renamed to `value` 24 | #' 25 | #' @author Christopher Brown 26 | #' 27 | #' @seealso 28 | #' - [setkeys()] : rename specific hash keys. 29 | #' - [hash()] : hash object 30 | #' 31 | #' @examples 32 | #' 33 | #' h <- hash( letters, 1:26 ) 34 | #' keys(h) # letters 35 | #' 36 | #' names(h) # same 37 | #' 38 | #' #' Rename keys 39 | #' # keys( h ) <- rev( keys(h) ) 40 | #' 41 | #' @docType methods 42 | #' @rdname keys 43 | #' @aliases keys keys-methods 44 | #' @export 45 | 46 | setGeneric( "keys", function(x, ...) standardGeneric("keys") ) 47 | 48 | 49 | #' @rdname keys 50 | #' @aliases keys,hash-method 51 | 52 | setMethod( "keys" , "hash" , 53 | function(x, sorted=getOption('hash.sorted', FALSE) ) ls(x@.Data, all.names=T, sorted=sorted ) 54 | ) 55 | 56 | #' @rdname keys 57 | #' @aliases names names.hash 58 | #' @export 59 | 60 | setMethod( "names", "hash", function(x) base::names( x@.xData ) ) 61 | 62 | 63 | # ---------------------------------------------------- 64 | # REPLACE METHOD 65 | #' @rdname keys 66 | #' @docType methods 67 | #' @aliases keys<--methods 68 | #' @export 69 | 70 | setGeneric( "keys<-", function(x,value) standardGeneric( "keys<-") ) 71 | 72 | 73 | #' @name keys<-,hash,ANY-method 74 | #' @rdname keys 75 | 76 | setReplaceMethod( "keys", c('hash','ANY'), 77 | function( x, value) { 78 | value <- as.character(value) 79 | setkeys(x, keys(x), value ) 80 | } 81 | ) 82 | 83 | 84 | #' @name keys<-,hash,ANY-method 85 | #' @rdname keys 86 | 87 | setReplaceMethod( "keys", c('hash','ANY'), 88 | function( x, value) { 89 | value <- as.character(value) 90 | setkeys(x, keys(x), value ) 91 | } 92 | ) 93 | 94 | 95 | #' @name keys<-,hash,NULL-method 96 | #' @rdname keys 97 | 98 | setReplaceMethod( "keys", c('hash','NULL'), 99 | function( x, value) 100 | warning( "Keys cannot be set to NULL. " , 101 | "If you wish to delete the hash entry use the `del` method." 102 | ) 103 | ) 104 | 105 | 106 | -------------------------------------------------------------------------------- /R/kv.R: -------------------------------------------------------------------------------- 1 | #' Key value iteration 2 | #' 3 | #' Create a list with `k` and `v` elements that is useful for 4 | #' explicit iteration 5 | #' 6 | #' @param x object such as vector or list to separate into key value pairs 7 | #' @param ... additional arguments 8 | #' @details 9 | #' 10 | #' No magic here, just something simple to convert `x` into a list of 11 | #' lists. Each element of `x` is broken into a list with elements `k` 12 | #' (key) and `v` (value). See examples. 13 | #' 14 | #' For many cases, key-value iteration can be done with `*apply` or 15 | #' `paste` functions. This is made to be explicit and work on a variery of 16 | #' objects. 17 | #' 18 | #' @references 19 | #' * \cr 20 | #' * \cr 21 | #' 22 | #' @return 23 | #' A named list of list; each element of `x` becomes a one element list 24 | #' with elements `k` and `v` representing the keys and values 25 | #' 26 | #' @examples 27 | #' 28 | #' h <- hash(a=1,b=2,c=3) 29 | #' kv(h) 30 | #' 31 | #' for( kv in kv(h) ) 32 | #' cat( kv$k, ":", kv$v, "\n") 33 | #' 34 | #' @export 35 | 36 | kv <- function(x, ...) UseMethod('kv') 37 | 38 | #' @rdname kv 39 | #' @export 40 | kv.hash <- function(x, ...) { 41 | x <- values(x) 42 | kv =list() 43 | for( i in 1:length(x) ) { 44 | kv[[i]] = list( k=names(x)[[i]], v=x[[i]] ) 45 | } 46 | names(kv)=names(x) 47 | kv 48 | } 49 | 50 | -------------------------------------------------------------------------------- /R/length.R: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------- 2 | # length.R 3 | # return the number of keys in a hash 4 | # NB: 5 | # - This doesn't work: env.profile(x@.Data)$nchains 6 | # --------------------------------------------------------------------- 7 | 8 | #' length Returns the number of key-value pairs in a hash 9 | #' 10 | #' Returns the number of items in a hash 11 | #' 12 | #' Return the number of items in the hash by calling [length()] on 13 | #' the internal environment. 14 | #' 15 | #' @aliases length 16 | #' @return integer Number of items in the hash. 17 | #' @author Christpher Brown 18 | #' @seealso See Also [hash()], [length()] 19 | #' @keywords methods 20 | #' @examples 21 | #' 22 | #' 23 | #' h <- hash( letters, 1:26 ) 24 | #' length(h) # 26 25 | #' 26 | #' @rdname hash 27 | 28 | setMethod( "length", "hash", function(x) length( x@.Data ) ) 29 | 30 | 31 | -------------------------------------------------------------------------------- /R/make_keys.R: -------------------------------------------------------------------------------- 1 | #' creates/coerces objects to proper hash keys 2 | #' 3 | #' **Notes:** 4 | #' - This function is not exported. 5 | #' - `make.keys()` will be deprecated in a future release; Use `make_keys()` 6 | #' instead. 7 | #' 8 | #' @param key An object that represents the key(s) to be coerced to a valid 9 | #' hash keys. 10 | #' 11 | #' @details 12 | 13 | #' Given a vector of any type, `make_keys` tries to coerce it into a 14 | #' character vector that can be used as a hash key. It is just a wrapper 15 | #' around [base::as.character()] with some additional error checking. 16 | #' 17 | #' #' `make_keys` will replace `make.keys` in a future release. 18 | #' 19 | #' @note 20 | #' This is used internally by the hash package and should not be normally 21 | #' needed. It is *not* exported. 22 | #' 23 | #' @return A character vector of valid keys/names 24 | #' @seealso 25 | #' - [base::as.character()] 26 | #' - [base::make.names()] 27 | #' 28 | #' @examples 29 | #' 30 | #' \dontrun{ 31 | #' make_keys( letters ) 32 | #' make_keys( 1:26 ) 33 | #' } 34 | # @export 35 | make_keys <- function(key) { 36 | 37 | key <- as.character(key) 38 | 39 | if( length(key) == 0 ) 40 | stop("You must provide at least one key to the hash") 41 | 42 | if( any(key=="") ) 43 | stop( 44 | "\nThe empty character string, '', cannot be used for a key at key(s): ", 45 | paste( which( key == "" ), collapse=", " ) 46 | ) 47 | 48 | key 49 | } 50 | 51 | 52 | #' @rdname make_keys 53 | # @export 54 | 55 | make.keys <- make_keys -------------------------------------------------------------------------------- /R/set.R: -------------------------------------------------------------------------------- 1 | # set -assign key-value pair(s) to a hash 2 | # 3 | # \code{.set} is an _internal method_ for assigning key-value pairs to a 4 | # \code{\link{hash}}. Normally, there is no need to use this function. 5 | # Convenient access is provided by: \code{$}, \code{[}, \code{[[}, 6 | # \code{values} and their corresponding replacement methods. 7 | # 8 | # @param hash An hash object on which to set the key-value pair(s) 9 | # @param ... Any of several ways to specify keys and values. See Details. 10 | # 11 | # \code{.set} takes 4 types of arguments: explicitly named key and value 12 | # vectors named key-value pairs named vectors implicit key-value pairs 13 | # 14 | # The keys are automatically coerced to valid keys and are restricted to 15 | # character classes. Values are free to be any valid R object. 16 | # 17 | # 18 | # \code{.set} sets zero or more key-value pairs. If the key(s) already exist, 19 | # existing values are silently clobbered. Otherwise, a new value is saved for 20 | # each key. Keys and values are by the \code{...} argument. If \code{...} is: 21 | # 22 | # made only of explicitly named \code{keys} and \code{values} arguments then 23 | # these are taken as the keys and values respectively. 24 | # 25 | # a named list, then the names are taken as keys and list elements are taken 26 | # as values. 27 | # 28 | # a named vector, then the names are taken as keys. Vector elements are taken 29 | # as values. 30 | # 31 | # of length two, keys are taken from the first element, values from the 32 | # second. 33 | # 34 | # Keys are coerced to type \code{character}. 35 | # 36 | # Keys and values are assigned to the hash as follows: 37 | # 38 | # IF \code{keys} and \code{values} are the same length, key-value pairs are 39 | # added to the hash pairwise. 40 | # 41 | # IF \code{keys} is a vector of length 1, then this key is assigned the entire 42 | # \code{values} vector. 43 | # 44 | # IF \code{values} is a vector of length 1, each key of \code{keys} is 45 | # assigned the value given by \code{values} 46 | # 47 | # IF \code{keys} and \code{values} are of different lengths, both greater than 48 | # one, then the assignment is considered ambiguous and an error is thrown. 49 | # 50 | # @return 51 | # \code{.set} exists solely for its side-effects. An invisible NULL 52 | # is returned. 53 | # 54 | # @author Christopher Brown 55 | # 56 | # @seealso \code{\link{hash}}, \code{\link{environment}} 57 | # 58 | # @examples 59 | # 60 | # h <- hash() 61 | # 62 | # \dontrun{ 63 | # hash:::.set( h, keys=letters, values=1:26 ) 64 | # hash:::.set( h, a="foo", b="bar", c="baz" ) 65 | # hash:::.set( h, c( aa="foo", ab="bar", ac="baz" ) ) 66 | # clear(h) 67 | # hash:::.set( h, letters, values ) 68 | # } 69 | # 70 | # @name set 71 | # @rdname set 72 | # @aliases set .set 73 | 74 | .set <- 75 | function( hash, ... ) { 76 | 77 | li <- list(...) 78 | 79 | # EXPLICIT 'keys' AND 'values' ARGUMENTS 80 | # .set( keys=letters, values=1:26 ) 81 | # if( identical( names(li) , c('keys', 'values') ) ) { 82 | if( 83 | 'keys' %in% names(li) && 84 | 'values' %in% names(li) 85 | ) { 86 | keys <- li[['keys']] 87 | 88 | 89 | values <- li[['values']] 90 | } else 91 | 92 | # NAMED KV PAIRS 93 | # .set( a=1, b=2, c=3 ) 94 | if( ! is.null( names(li) ) ) { 95 | keys <- names(li) 96 | values <- li 97 | if( length(values) == 1 ) values <- li[[1]] #14 98 | } else 99 | 100 | # NAMED VECTOR: 101 | # .set( c(a=1, b=2, c=3) ) 102 | if( length(li) == 1 ) { 103 | v <- li[[1]] 104 | if( length(names(v) == length(v) ) ) { 105 | keys <- names(v) 106 | values <- v 107 | } 108 | } else 109 | 110 | # IMPLICIT keys AND values VECTORS 111 | if( length(li) == 2 ) { 112 | keys <- li[[1]] 113 | values <- li[[2]] 114 | } 115 | 116 | keys <- make_keys(keys) 117 | 118 | 119 | # UNEQUAL keys and values both greater than one 120 | if ( 121 | length(keys) > 1 & 122 | length(values) > 1 & 123 | length(keys) != length(values) 124 | ) { 125 | stop( 126 | "Keys of length ", length( keys ), 127 | " do not match values of length ", length( values ) , 128 | "\n" 129 | ) 130 | } 131 | 132 | 133 | # ASSIGNMENT: 134 | 135 | if( length(keys) == 1 ) { 136 | 137 | assign( keys, values, envir = hash@.Data ) 138 | 139 | } else if( length( keys ) == length( values ) ) { 140 | 141 | for( i in 1:length(keys) ) 142 | assign( keys[[i]], values[[i]], envir = hash@.Data ) 143 | # assign( keys[[i]], hash( b=12 ), envir = hash@.Data ) 144 | 145 | } else { 146 | 147 | if( length( keys ) == 1 ) 148 | assign( keys, values, envir = hash@.Data ) 149 | 150 | if( length( values ) == 1 ) 151 | for( i in 1:length(keys) ) 152 | assign( keys[[i]], values, envir = hash@.Data ) 153 | } 154 | 155 | return( invisible(NULL) ) 156 | 157 | } 158 | -------------------------------------------------------------------------------- /R/setkeys.R: -------------------------------------------------------------------------------- 1 | #' setkeys - change/rename the keys of a hash 2 | #' 3 | #' Changes the keys of a hash from `old` keys to `new` 4 | #' 5 | #' @param x hash object to rename keys on 6 | #' @param old character (or coerciable to character); old keys/names 7 | #' @param new character (or coerciable to character); new keys/names 8 | #' 9 | #' This methods renames keys in a hash. If there is a collision between *old* 10 | #' and *new* names, the old names are first copied to a temporary slot to 11 | #' ensure 12 | #' 13 | #' `setkeys` is S4 generic so that other packages might also use the 14 | #' generic functions 15 | #' 16 | #' @return Invisiblly returns `x` with its keys renamed. 17 | #' 18 | #' @seealso 19 | #' - [keys()] 20 | #' - [hash()] 21 | #' 22 | #' @examples 23 | #' 24 | #' h <- hash( letters, 1:26 ) 25 | #' 26 | #' h2 <- copy(h) 27 | #' setkeys( h2, keys(h), paste0( keys(h), "1" ) ) 28 | #' h2 29 | #' 30 | #' h3 <- copy(h) 31 | #' setkeys( h3, keys(h), rev( keys(h) ) ) 32 | #' h3 33 | #' 34 | #' @note setkeys is modeled after `setnames` in the data.table package. 35 | #' @name setkeys 36 | #' @rdname setkeys 37 | #' @export 38 | 39 | setGeneric( 'setkeys', function(x, old, new) standardGeneric("setkeys") ) 40 | 41 | 42 | #' @name setkeys,hash,ANY,ANY-method 43 | #' @aliases setkeys,hash-method 44 | #' @rdname setkeys 45 | 46 | setMethod( "setkeys", c("hash","ANY","ANY"), 47 | function( x, old, new ) { 48 | 49 | old <- as.character(old) 50 | new <- as.character(new) 51 | 52 | 53 | # old keys don't match existing keys 54 | if( any( ! old %in% keys(x) ) ) { 55 | wh <- old[ which( ! old %in% keys(x) ) ] 56 | stop( 57 | "\n keys: " 58 | ,paste0( wh, ",", collapse=" " ) 59 | , " not found in ", deparse(substitute(x)) 60 | ) 61 | } 62 | 63 | # old = new 64 | if( all(old==new) ) return( invisible(x) ) 65 | 66 | # duplicated keyname in new. 67 | if( any( wh <- duplicated( new) ) ) 68 | stop( "duplicated keys names were supplied to setkeys - ", paste0( new[wh], collapse=", " ) ) 69 | 70 | # old and new don't align 71 | if( length(old) != length(new) ) 72 | stop( "Length of old and new keys differ." ) 73 | 74 | 75 | # Detect collison of new keys with old. If they 76 | # exist create an intermediate set of names. 77 | intermediate <- new 78 | while( any( intermediate %in% old ) ) 79 | if( is.null(intermediate) ) 80 | intermediate <- paste0( "..", new ) else 81 | intermediate <- paste0( "..", intermediate ) 82 | 83 | if( any( intermediate != new ) ) { 84 | 85 | for( i in 1:length(old) ) 86 | assign( intermediate[[i]], x[[ old[[i]] ]], x ) 87 | 88 | for( i in 1:length(old) ) 89 | del( old[[i]], x ) 90 | 91 | for( i in 1:length(old) ) 92 | assign( new[[i]], x[[ intermediate[[i]] ]], x ) 93 | 94 | for( i in 1:length(old) ) 95 | del( intermediate[[i]], x ) 96 | 97 | } else { 98 | 99 | for( i in 1:length(old) ) { 100 | assign( new[[i]], x[[ old[[i]] ]], x ) 101 | del( old[[i]], x ) 102 | } 103 | 104 | } 105 | 106 | invisible(x) 107 | 108 | } 109 | 110 | ) 111 | -------------------------------------------------------------------------------- /R/show.R: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # show.r 3 | # 4 | # METHODS: show 5 | # The default method on the class. Perhaps this should return the 6 | # length. 7 | # 8 | # See Also: print 9 | # ----------------------------------------------------------------------------- 10 | setMethod( "show" , "hash" , 11 | function(object) cat(format(object)) 12 | ) 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /R/values.R: -------------------------------------------------------------------------------- 1 | #' values 2 | #' 3 | #' Get/set values for a hash object 4 | #' 5 | #' @param x hash object 6 | #' @param keys character; names of keys to get 7 | #' @param value to be set 8 | #' @param ... Unused. 9 | #' 10 | #' The `values` method returns a named-list of key value pairs from a hash. 11 | #' If a hash is desired, use the hash slice method, `h[x]`. 12 | #' 13 | #' List elements are named after the corresponding key. In previous versions, 14 | #' the return was simplified. This is no longer the case. Users should 15 | #' simplify arguments if needed using `unlist` or similar. See examples. 16 | #' 17 | #' If argument `keys` is provided, only these keys are 18 | #' returned. This also allows for returning values mulitple times as in: 19 | #' 20 | #' ` values(h, keys=c( 'a','a','b' ) ) ` 21 | #' 22 | #' This is now the preferred method for returning multiple values for the same 23 | #' key. 24 | #' 25 | #' The replacement method, `values<-` can replace all the values or simply 26 | #' those associated with the supplied `keys`. Use of the accessor '[' is 27 | #' almost always preferred. 28 | #' 29 | #' @return 30 | #' 31 | #' A named list. Names are those of the keys, values are the associated hash 32 | #' values. 33 | #' 34 | #' @seealso 35 | #' - [extract] for R-like accessors 36 | #' 37 | #' @examples 38 | #' h <- hash( letters, 1:26 ) 39 | #' values(h) # 1:26 40 | #' 41 | #' h <- hash( 1:26, letters ) 42 | #' values(h) 43 | #' values(h, keys=1:5 ) 44 | #' values(h, keys=c(1,1,1:5) ) 45 | #' 46 | #' values(h, keys=1:5) <- 6:10 47 | #' values(h) <- rev( letters ) 48 | #' 49 | #' # When values are obejcts 50 | #' h <- hash( c('a','b'), Sys.time() ) 51 | #' class(h$a) # "POSIXct" "POSIXt" 52 | #' vals <- values( h ) 53 | #' class( unlist(vals) ) # numeric 54 | #' class( Reduce( c, vals ) ) # "POSIXct" "POSIXt" 55 | #' 56 | #' vals <- values(h) 57 | #' class(vals) # List 58 | #' class( Reduce( c, vals ) ) # "POSIXct" "POSIXt" 59 | #' 60 | # @name values 61 | #' @rdname values 62 | # @docType methods 63 | #' @export 64 | 65 | setGeneric( 'values', function(x, ...) standardGeneric('values') ) 66 | 67 | 68 | #' @name values,hash-method 69 | #' @rdname values 70 | 71 | setMethod( 'values', 'hash', 72 | function(x, keys=NULL, ... ) { 73 | if( is.null(keys) ) keys <- keys(x) 74 | if( ! is.character(keys) ) keys <- make_keys(keys) 75 | 76 | # structure( 77 | # # sapply( keys, get, x, ..., simplify=FALSE, USE.NAMES = FALSE ), 78 | # sapply( keys, get, x, ..., simplify=FALSE, USE.NAMES = FALSE ), 79 | # names=keys 80 | # ) 81 | 82 | structure( 83 | mget( keys, envir=x@.xData ) 84 | , names=keys 85 | ) 86 | } 87 | ) 88 | 89 | 90 | 91 | # @name values<- 92 | #' @rdname values 93 | #' @export 94 | 95 | setGeneric( 'values<-', function(x, ..., value) standardGeneric( 'values<-' ) ) 96 | 97 | 98 | # @name values<-,hash,ANY-mehtod 99 | # @aliases values<-,hash-method 100 | #' @rdname values 101 | #' @export 102 | 103 | setReplaceMethod( 'values', c('hash', 'ANY' ), 104 | function(x, ..., value ) { 105 | keys <- list(...)$keys 106 | if ( is.null(keys) ) keys <- keys(x) 107 | if ( ! is.character(keys) ) keys <- make_keys(keys) 108 | 109 | x[ keys ] <- value 110 | x 111 | } 112 | ) 113 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | .onAttach <- function( libname, pkgname ) { 2 | 3 | dt <- read.dcf( system.file("DESCRIPTION", package = pkgname ), "Date" ) 4 | yr <- substr(dt,1,4) 5 | version <- read.dcf( system.file("DESCRIPTION", package = pkgname ), "Version" ) 6 | 7 | if( interactive() ) 8 | packageStartupMessage( 9 | pkgname 10 | , "-", version 11 | , ifelse( ! is.na(dt), paste0(' (',dt,')'), '' ) 12 | , " - Copyright \u00a9 ", ifelse(! is.na(yr), yr, '') 13 | , " Decision Patterns" 14 | , domain = NA 15 | ) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hash 2 | 3 | ## hash/dictionary/maps in R 4 | 5 | ![](https://img.shields.io/cran/v/hash.svg) 6 | ![](https://img.shields.io/cran/l/hash.svg) 7 | [![lifecycle](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) 8 | [![Downloads](https://cranlogs.r-pkg.org/badges/hash?color=brightgreen)](https://www.r-pkg.org/pkg/hash) 9 | [![](http://cranlogs.r-pkg.org/badges/grand-total/hash)](https://cran.r-project.org/package=hash) 10 | 11 | 12 | The *hash* package provides a fully-functional hash/dictionaryfor the R language. It provides richer features and finer control of hash behavior than using native R structures like list or environments and has as a user-friendly interface. Performance-wise it has similar and sometimes better performance than these structures especially for larger objects. 13 | 14 | 15 | ## Installation 16 | 17 | Latest Release: 18 | 19 | install.packages('hash') 20 | 21 | 22 | Development Version: 23 | 24 | install.packages('devtools') 25 | devtools::install_github('decisionpatterns/r-hash') 26 | 27 | 28 | ## Examples 29 | 30 | # Create a hash 31 | h <- hash(a=1, b=2, c=3) 32 | h <- hash( letters[1:3], 1:3 ) 33 | h <- hash( list(a=1,b=2,c=3) ) 34 | 35 | # Keys 36 | keys(h) 37 | 38 | # Values (named list) 39 | values(h) 40 | 41 | # Assign to single key hash 42 | h$a <- "foo" 43 | h[['a']] <- "bar" 44 | 45 | # Slice 46 | h[ c('a','c') ] 47 | 48 | 49 | ## Allowable Values 50 | 51 | 52 | **KEYS** must be a valid character value and may not be the empty string (""). Keys must be unique. 53 | 54 | **VALUES** can be any R value, vector, object, etc. 55 | 56 | 57 | ## Usage Notes 58 | 59 | Hashes probably work about how you would expect, but since there are built from R's native environments. There are three things to Remember: 60 | 61 | **PASS-BY REFERENCE**. hashes are environments, special objects in R where only one copy exists globally. When passed as an argument to a function, no local copy is made and any changes to the hash in the functions are reflected globally, i.e. in the caller's namespace. 62 | 63 | **PERFORMANCE**. Hashes are designed to be exceedingly fast using R environment's internal hash table. The hash function is not without its cost. For small data structures, a named lists and vectors will out-perform a hash in nearly every case. After approximately 500+ elements, the performance of the hash becomes faster than native lists and vectors. 64 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO: 2 | 3 | - [] fix argument order has.key(x, hash) to has_key(hash,x) 4 | 5 | - [] Fix behavior on values to reduce and retain class 6 | - implement drop argument? Is there a use case when drop would reduce the 7 | object to something other than a hash? 8 | 9 | - implement mget in slice operation 10 | 11 | - Create a mapping from Python's dictionary to hash package 12 | c.f. http://docs.python.org/2/library/stdtypes.html#dictionary-view-objects 13 | 14 | - automatic coercion for hash[ x , ... ] where x is a 'name' 15 | 16 | - Is there a happly function that is like lapply that either passes keys and 17 | values to the functions happly( h, function(k,v,...) ), will it return a 18 | hash object? 19 | 20 | 21 | - [x] Increase perfomance of `[` and `hash` methods 22 | 23 | - [x] Be able to create a loop that assigns both key and value, e.g.: 24 | for( k,v in ha ) ... 25 | - use itertools to iterate over keys and values 26 | 27 | 28 | ## Defaults 29 | 30 | - (LOW PRIORITY) default MISSING behavior should be customizable at the hash 31 | instance level. 32 | Requires a slot: @na.action that can be a function or value. This default 33 | should exist between the global default and the call level. 34 | This is low priority since missing customization only applies to hash slices 35 | now. 36 | 37 | x Default of missing key should be NULL. 38 | - option to fail on missing key? 39 | - customizable missing 40 | 41 | - Should the default value of non-existant keys be NA or NULL. 42 | - The default of the environemnet is NULL 43 | - NA is the norm when data is missing, i.e. from a frame. 44 | - N.B. When access these objects the missing values are returned: 45 | - vectors : NA 46 | - list : NULL 47 | - data.frames : NULL 48 | - since getOption( 'na.action.hash' ) returns NULL if undefined, we can 49 | use this as getOption( 'na.action.hash' ) as the na.action only . . . 50 | but this sets the value of the Hash to NULL. Setting the value to NULL 51 | is equal to deleting the key. h$key <- NULL deletes the key. 52 | 53 | - [x] Handle any type for a key, especially integers. 54 | 55 | - [?] Keep track of the type of the key: `hash@key.class` 56 | Asking for `keys(h)` would return the original type via coercion. 57 | 58 | - [x] Method to coerce value to key 59 | even through the make.key or make.name functions. 60 | ? Do we allow mixing of types ? :: NO 61 | 62 | 63 | - [?] IxHash : Indexed Hash. Allow access by integer position? 64 | There becomes a problem of keeping track of replacements. 65 | 66 | - [ ] Keys can have muliple values / be recursive? 67 | how would this be implemented as an md5hash of the args? 68 | h[[ vector ]] <- values ? 69 | h[[ paste( as.character( vector ) ) ]] <- value 70 | How can we make each of the keys searchable? i.e. get all where the second 71 | key field = 7. Each of the key fields would have to be hashed? 72 | 73 | - Coersion functions 74 | - as.vector, 75 | - pairlist, 76 | - [x] as.data.frame, 77 | - [x] as.environment, 78 | - [x] as.list 79 | 80 | - other coercions : 81 | as.environment, not possible without clobbering base h@env anyhow 82 | as.vector , yes 83 | 84 | - Implement clear as initializing of hash rather than rm 85 | 86 | - R/clear.R bug? :: SHOULD THIS COPY THE HASH? 87 | h <- hash( letters, 1:26 ) 88 | hh <- h 89 | clear(hh) 90 | h # EMPTY. 91 | 92 | Does not work the same with 'rm'. Will this require overloading the 93 | assignment operators? 94 | 95 | - test functions 96 | x is.hash : object is a hash 97 | is.vector : test values to see if they are expressible as a vector 98 | is.list : " 99 | 100 | - assign <==> set : it is to bad that assign is used instead of get 101 | get/set seems better aligned than get/assign. 102 | 103 | 104 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Check results 2 | 3 | * Package CITAN requires external library libgtk-2.8+ 4 | * Package KoNLP requires rJava requiring external library 5 | * Package MXM uses internal/non-exported function .set 6 | Maintainer contacted 7 | * Package orderbook does not instal 8 | 9 | ========================================================================= 10 | Reverse dependency check for hash 3.0.2 11 | ========================================================================= 12 | setting value 13 | version R version 3.1.0 (2014-04-10) 14 | system x86_64, linux-gnu 15 | ui RStudio (0.98.1060) 16 | language en 17 | collate en_US.UTF-8 18 | tz 19 | 20 | package * version date source 21 | chron 2.3.45 2014-02-11 CRAN (R 3.1.0) 22 | data.table * 1.9.4 2014-10-02 CRAN (R 3.1.0) 23 | devtools * 1.6.0.9000 2014-10-30 Github (hadley/devtools@5f0e3ed) 24 | digest 0.6.4 2013-12-03 CRAN (R 3.1.0) 25 | hash * 3.0.2 local 26 | magrittr * 1.0.1 2014-05-15 CRAN (R 3.1.0) 27 | memoise 0.2.1 2014-04-22 CRAN (R 3.1.0) 28 | plyr 1.8.1 2014-02-26 CRAN (R 3.1.0) 29 | Rcpp 0.11.1 2014-03-14 CRAN (R 3.1.0) 30 | RCurl 1.95.4.1 2013-03-06 CRAN (R 3.1.0) 31 | reshape2 1.4 2014-04-23 CRAN (R 3.1.0) 32 | roxygen2 4.0.2 2014-09-02 CRAN (R 3.1.0) 33 | rstudioapi 0.1 2014-03-27 CRAN (R 3.1.0) 34 | stringr 0.6.2 2012-12-06 CRAN (R 3.1.0) 35 | 36 | CITAN =================================================================== 37 | * checking package dependencies ... ERROR 38 | Package required but not available: ‘RGtk2’ 39 | 40 | See the information on DESCRIPTION files in the chapter ‘Creating R 41 | packages’ of the ‘Writing R Extensions’ manual. 42 | 43 | 44 | GABi ==================================================================== 45 | 46 | 47 | 48 | HAP.ROR ================================================================= 49 | 50 | 51 | 52 | KoNLP =================================================================== 53 | * checking package dependencies ... ERROR 54 | Package required but not available: ‘rJava’ 55 | 56 | See the information on DESCRIPTION files in the chapter ‘Creating R 57 | packages’ of the ‘Writing R Extensions’ manual. 58 | 59 | 60 | MXM ===================================================================== 61 | * checking examples ... ERROR 62 | Running examples in ‘MXM-Ex.R’ failed 63 | The error most likely occurred in: 64 | 65 | > base::assign(".ptime", proc.time(), pos = "CheckExEnv") 66 | > ### Name: SES 67 | > ### Title: Feature selection algorithm for identifying multiple minimal, 68 | > ### statistically-equivalent and equally-predictive feature signatures. 69 | > ### Aliases: SES 70 | > ### Keywords: SES Multiple Feature Signatures Feature Selection Variable 71 | > ### Selection 72 | > 73 | > ### ** Examples 74 | > 75 | > set.seed(123) 76 | > #require(gRbase) #for faster computations in the internal functions 77 | > require(hash) 78 | Loading required package: hash 79 | hash-3.0.2 provided by Decision Patterns 80 | > 81 | > #simulate a dataset with continuous data 82 | > dataset <- matrix(nrow = 1000 , ncol = 300) 83 | > dataset <- apply(dataset, 1:2, function(i) runif(1, 1, 100)) 84 | > 85 | > #define a simulated class variable 86 | > target = 3*dataset[,10] + 2*dataset[,200] + 3*dataset[,20] + runif(1, 0, 1); 87 | > 88 | > #define some simulated equivalences 89 | > dataset[,15] = dataset[,10] 90 | > dataset[,250] = dataset[,200] 91 | > dataset[,230] = dataset[,200] 92 | > 93 | > #run the SES algorithm 94 | > sesObject <- SES(target , dataset , max_k=5 , threshold=0.2 , test="testIndFisher", 95 | + hash = TRUE, hashObject=NULL); 96 | 97 | Conditional independence test used: testIndFisher 98 | error in try catch of the testIndFisher test 99 | Here's the original error message: 100 | could not find function ".set"Error in value[[3L]](cond) : 101 | Calls: SES ... tryCatch -> tryCatchList -> tryCatchOne -> 102 | Execution halted 103 | 104 | 105 | neuroim ================================================================= 106 | 107 | 108 | 109 | orderbook =============================================================== 110 | * checking whether package ‘orderbook’ can be installed ... ERROR 111 | Installation failed. 112 | See ‘/tmp/RtmpH4OO6G/check_cran6a467fa2b45c/orderbook.Rcheck/00install.out’ for details. 113 | 114 | 115 | rpartitions ============================================================= 116 | 117 | 118 | 119 | stream ================================================================== -------------------------------------------------------------------------------- /dev/hash.actions.R.off: -------------------------------------------------------------------------------- 1 | 2 | # --------------------------------------------------------------------- 3 | # hash.na.action. functions 4 | # set options( hash.na.action=X ) 5 | # 6 | # X can be a function that expects minimally the name of a key or 7 | # a constant. 8 | # 9 | # These can be used in .get or the various accessor functions. Can 10 | # be globally set options( 'hash.na.action' ) 11 | # 12 | # --------------------------------------------------------------------- 13 | # FAIL on missing key 14 | hash.fail <- function(key, call.=FALSE ) { 15 | stop( "key, ", key, ", not found in hash.", call.=call. ) 16 | } 17 | 18 | # WARN on missing key 19 | hash.warn <- function(key, call.=FALSE, immediate.=TRUE ) { 20 | warning( "key, ", key, ", not found in hash.", call.=call., immediate.=immediate. ) 21 | return( hash.default() ) 22 | } 23 | 24 | # DEFAULT: NA on missing key 25 | hash.default <- function(key) return(NULL) 26 | 27 | -------------------------------------------------------------------------------- /dev/hash.actions.Rd.off: -------------------------------------------------------------------------------- 1 | \name{hash.action} 2 | \Rdversion{1.1} 3 | \alias{hash.default} 4 | \alias{hash.fail} 5 | \alias{hash.warn} 6 | 7 | \title{Actions for when hash keys are not found.} 8 | 9 | \description{ 10 | These functions control the behavior of the hash package when trying 11 | to access non-existant hash keys. The default behavior is to return 12 | \code{NA}. This is controllable through the \code{hash.action} 13 | option. See Details for further explanation. 14 | } 15 | 16 | \usage{ 17 | hash.default( key ) 18 | hash.warn( key, call. = FALSE, immediate. = TRUE ) 19 | hash.fail( key, call. = FALSE ) 20 | } 21 | 22 | \arguments{ 23 | 24 | \item{key}{ The name of the non-existant key } 25 | \item{call.}{ Passed to \code{\link{stop}} or \code{\link{warning}} } 26 | \item{immediate.}{ 27 | hash.warn only. Whether to immediately issue the warning. See 28 | \code{\link{warning}} for details 29 | } 30 | } 31 | 32 | \details{ 33 | 34 | \code{hash.default} always returns \code{NA}. This is the default 35 | behavior for hashes. Looking up values for non-existant keys returns 36 | \code{NA}. 37 | 38 | \code{hash.warn} issues an immediate warning, but returns 39 | \code{hash.default} value, by default \code{NA}. 40 | 41 | \code{hash.fail} stops iexecution with an error. 42 | 43 | The default behavior can be customized by setting the \code{hash.action} 44 | option. A value or callback function may be provided. 45 | 46 | \code{ options( hash.action=0 ) } 47 | \code{ options( hash.action=hash.warn ) } 48 | 49 | } 50 | 51 | \value{ 52 | \code{hash.default} returns \code{NA}. 53 | \code{hash.warn} returns \code{hash.default()} with a warning. 54 | \code{hash.fail} fails immediately 55 | } 56 | 57 | \author{ 58 | Christopher Brown 59 | } 60 | 61 | 62 | 63 | \examples{ 64 | hash.default() 65 | } 66 | 67 | \keyword{ methods } 68 | \keyword{ manip } 69 | -------------------------------------------------------------------------------- /inst/memory-test.r: -------------------------------------------------------------------------------- 1 | # memory-test.r 2 | # 3 | # Cf. Issue#16 4 | # Conclusion: Cannot confirm existence of memory leak. 5 | 6 | library(hash) 7 | library(pryr) 8 | 9 | size=1e4 10 | 11 | gc(full=TRUE) 12 | used_0 <- mem_used() # Baseline 13 | 14 | for( i in 1:20 ) { 15 | 16 | # Define Hash 17 | # Numeric Key => sample from normal dist. 18 | h <- hash( keys=sample(size), values=rnorm(size)) 19 | 20 | # Delete Hash 21 | rm(h) 22 | gc() 23 | 24 | } 25 | 26 | gc(full=TRUE) 27 | 28 | # Compare 29 | used_1 <- mem_used() 30 | 31 | used_0; used_1; 32 | 33 | ( used_2 - used_0 ) <= 0 34 | -------------------------------------------------------------------------------- /man/as.data.frame.hash.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/as.data.frame.hash.R 3 | \name{as.data.frame.hash} 4 | \alias{as.data.frame.hash} 5 | \title{Turn a hash into a data.frame} 6 | \usage{ 7 | \method{as.data.frame}{hash}(x, ..., key = "key", value = "value") 8 | } 9 | \arguments{ 10 | \item{x}{hash} 11 | 12 | \item{...}{additional arguments to \code{\link[=values]{values()}}} 13 | 14 | \item{key}{string; name for the column of keys (Default: "key").} 15 | 16 | \item{value}{string; name for the column of values (Default: "value")} 17 | } 18 | \value{ 19 | a data.frame with as many row as elements in \code{x} and two columns one for the 20 | keys (always character) and value a list column 21 | } 22 | \description{ 23 | Transform a hash into a nrowx2 data.frame 24 | } 25 | \examples{ 26 | 27 | h <- hash( a=1, b=1:2, c=1:3) 28 | as.data.frame(h) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /man/attach_hash.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/attach_hash.R 3 | \name{attach_hash} 4 | \alias{attach_hash} 5 | \alias{detach_hash} 6 | \alias{attach} 7 | \title{attach/detach a hash to the search path} 8 | \usage{ 9 | attach_hash(hash, name = deparse(substitute(hash)), ...) 10 | 11 | detach_hash(...) 12 | 13 | attach(what, name = deparse(substitute(what)), ...) 14 | } 15 | \arguments{ 16 | \item{hash, what}{hash object to attach to the search path.} 17 | 18 | \item{name}{string; name for the environment on the search path} 19 | 20 | \item{...}{additional arguments passed to \code{\link[base:attach]{base::attach()}} 21 | 22 | \code{attach_hash} attaches the environment part of a hash to the search path. 23 | The values become directly accessible by their keys. 24 | 25 | \code{hash:::attach()} is a non-exported versions of the same.} 26 | } 27 | \description{ 28 | attach/detach a hash to the search path 29 | } 30 | \examples{ 31 | h <- hash( a=1, b=2, c=3 ) 32 | 33 | attach_hash(h) 34 | search() 35 | detach_hash(h) 36 | search() 37 | 38 | hash:::attach( h ) 39 | b # 2 40 | b <<- 25 41 | b # 25 42 | h 43 | detach(h) 44 | 45 | } 46 | \seealso{ 47 | \itemize{ 48 | \item \code{\link[base:attach]{base::attach()}} 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /man/clear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clear.R 3 | \docType{methods} 4 | \name{clear} 5 | \alias{clear} 6 | \alias{clear,hash-method} 7 | \title{Removes all key-value pairs from a hash} 8 | \usage{ 9 | clear(x) 10 | 11 | \S4method{clear}{hash}(x) 12 | } 13 | \arguments{ 14 | \item{x}{A \code{hash} object.} 15 | } 16 | \value{ 17 | None. Method clear exists entirely for its side effects. 18 | } 19 | \description{ 20 | \code{clear} removes all key-values from a hash. 21 | } 22 | \details{ 23 | \code{clear} removes (\code{rm}) the key-value pairs on the hash. 24 | For large hashes it might be faster to reinitialize the hash, though this 25 | might cause memory leaks. 26 | } 27 | \note{ 28 | \code{clear} should be called prior to removing a hash. This ensures 29 | that the memory from the environment is freed. 30 | } 31 | \examples{ 32 | 33 | h <- hash( letters, 1:26 ) 34 | h # An object of type 'hash' containing 26 key-value pairs. 35 | clear(h) 36 | h # An object of type 'hash' containing 0 key-value pairs. 37 | 38 | } 39 | \seealso{ 40 | \itemize{ 41 | \item \code{\link[=del]{del()}} to remove specific key-values from the hash. 42 | \item \code{\link[=hash]{hash()}} to create a new hash 43 | } 44 | } 45 | \author{ 46 | Christopher Brown 47 | } 48 | -------------------------------------------------------------------------------- /man/copy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/copy.R 3 | \docType{methods} 4 | \name{copy} 5 | \alias{copy} 6 | \alias{copy,hash-method} 7 | \title{copy} 8 | \usage{ 9 | copy(x, ...) 10 | 11 | \S4method{copy}{hash}(x, ...) 12 | } 13 | \arguments{ 14 | \item{x}{hash} 15 | 16 | \item{...}{additional arguments 17 | 18 | Creatinga copy using the assingment operator, \code{<-}, does not work as 19 | expected, since hashes are based on environments and environments are 20 | reference objects in R. The assignment operator consequently creates a 21 | linked copy the original hash and not an independent copy. The \code{copy} 22 | method provides an identical unlinked copy of the hash.} 23 | } 24 | \value{ 25 | hash, copy of hash \code{x} 26 | } 27 | \description{ 28 | Create a \emph{copy} of a hash. 29 | } 30 | \examples{ 31 | h <- hash( a=1, b=2 ) 32 | g <- h 33 | g$a <- "foo" 34 | h$a # "foo" 35 | 36 | h_new <- copy( h ) 37 | h_new$a <- "bar" 38 | h$a # still "foo" 39 | 40 | } 41 | \seealso{ 42 | \code{\link[base:environment]{base::environment()}} 43 | } 44 | \author{ 45 | Christopher Brown 46 | } 47 | -------------------------------------------------------------------------------- /man/del.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/del.R 3 | \docType{methods} 4 | \name{del} 5 | \alias{del} 6 | \alias{delete} 7 | \alias{del-methods} 8 | \alias{delete-methods} 9 | \alias{del,ANY,hash-method} 10 | \alias{del,character,hash-method} 11 | \alias{delete,ANY,hash-method} 12 | \title{Remove key-value pair(s) from a hash} 13 | \usage{ 14 | del(x, hash) 15 | 16 | \S4method{del}{ANY,hash}(x, hash) 17 | 18 | \S4method{del}{character,hash}(x, hash) 19 | 20 | delete(x, hash) 21 | 22 | \S4method{delete}{ANY,hash}(x, hash) 23 | } 24 | \arguments{ 25 | \item{x}{An object that will be coerced to valid key(s) to be removed from 26 | the hash. \code{x} will be coerced to a valid hash keys using 27 | \code{\link[=make_keys]{make_keys()}}} 28 | 29 | \item{hash}{A \code{\link[=hash]{hash()}} object} 30 | } 31 | \value{ 32 | None. This method exists solely for the side-effects of removing 33 | items from the hash. 34 | } 35 | \description{ 36 | Removes key-value pair(s) from a hash by name of the object. There are also 37 | R-like methods described in \link{Extract}. To delete all keys, use 38 | \code{\link[=clear]{clear()}}. 39 | } 40 | \examples{ 41 | 42 | h <- hash( letters, 1:26 ) 43 | 44 | # USING del 45 | del( "a", h ) # delete key a 46 | del( c("b","c"), h ) # delete keys b, c 47 | 48 | # USING rm 49 | rm( "d", envir=h ) # delete key d 50 | rm( list= c("e","f"), envir=h ) # delete keys e,f 51 | 52 | # USING R syntsx 53 | h$g <- NULL # delete key g 54 | h[['h']] <- NULL # delete key h 55 | h['i'] <- NULL # delete key i 56 | h[ c('j','k')] <- NULL # delete keys e,f 57 | 58 | keys(h) 59 | D 60 | 61 | } 62 | \seealso{ 63 | \itemize{ 64 | \item \code{\link[base:rm]{base::rm()}} base function used by \code{del} 65 | \item \link{Extract} for R-like accessor 66 | \item \code{\link[=clear]{clear()}} to remove all key-values and return an empty hash 67 | \item \code{\link[=hash]{hash()}} 68 | } 69 | } 70 | \author{ 71 | Christopher Brown 72 | } 73 | \keyword{data} 74 | \keyword{manip} 75 | \keyword{methods} 76 | -------------------------------------------------------------------------------- /man/extract.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract.R 3 | \docType{methods} 4 | \name{extract} 5 | \alias{extract} 6 | \alias{[,hash,ANY,missing,missing-method} 7 | \alias{[,hash,missing,missing,missing-method} 8 | \alias{[<-,hash,ANY,missing,ANY-method} 9 | \alias{[<-,hash,ANY,missing,NULL-method} 10 | \alias{$<-,hash,NULL-method} 11 | \alias{[[<-,hash,ANY,missing,ANY-method} 12 | \alias{[[<-,hash,ANY,missing,NULL-method} 13 | \title{Extract} 14 | \usage{ 15 | \S4method{[}{hash,ANY,missing,missing}(x, i, j, ..., drop = TRUE) 16 | 17 | \S4method{[}{hash,missing,missing,missing}(x, i, j, ..., drop = TRUE) 18 | 19 | \S4method{[}{hash,ANY,missing,ANY}(x, i, j, ...) <- value 20 | 21 | \S4method{[}{hash,ANY,missing,`NULL`}(x, i, j, ...) <- value 22 | 23 | \S4method{$}{hash,`NULL`}(x, name) <- value 24 | 25 | \S4method{[[}{hash,ANY,missing,ANY}(x, i) <- value 26 | 27 | \S4method{[[}{hash,ANY,missing,`NULL`}(x, i) <- value 28 | } 29 | \arguments{ 30 | \item{x}{\code{\link[=hash]{hash()}} object} 31 | 32 | \item{i}{keys to get or set} 33 | 34 | \item{j}{unused; retained to be compatoble with base package} 35 | 36 | \item{...}{Arguments passed to additional methods \code{\link[=sapply]{sapply()}}} 37 | 38 | \item{drop}{unused; retained to be compatible with base package} 39 | 40 | \item{value}{the value to set for the key-value pair} 41 | 42 | \item{name}{the key name 43 | 44 | \code{$} is a look-up operator for a single key. The base \code{$} method 45 | are used directly on the inherited environment. The supplied key is taken 46 | as a string literal and is not interpreted. The replaement form, \code{$<-} 47 | mutates the hash in place. 48 | 49 | \code{[[} is the look-up, extraction operator. It returns the value of a 50 | single key and will interpret its argument. The replacement method, 51 | \code{[[<-} mutates the hash in place. 52 | 53 | \code{[} is a slice operator. It returns a hash with the subset of key-value 54 | pairs. Unlike the other accessor methods, \code{[} returns a \emph{copy}. 55 | 56 | All hash key misses return \code{NULL}. All hash key replacements with NULL 57 | delete the key-value pair from the hash. 58 | 59 | \code{$} and \code{[[} return the value for the supplied argument. If 60 | \code{i} is not a key of \code{x}, \code{NULL} is returned with a warning. 61 | 62 | \code{[} returns a hash slice, a subhash copy \code{x} with only the keys 63 | \code{i} defined. 64 | 65 | See details above for the complete explanation.} 66 | } 67 | \description{ 68 | These are the hash accessor methods. They closely follow the R style. 69 | } 70 | \examples{ 71 | 72 | h <- hash( c('a','b','c'), 1:3 ) 73 | 74 | # NAMED ACCESS 75 | 76 | h$a # 1 77 | h$c # 3 78 | 79 | # class of values change automatically 80 | class(h$a) # integer 81 | h$a <- 1.1 82 | class(h$a) # numeric 83 | 84 | # values can contain more complex objects 85 | h$a <- 1:6 86 | h 87 | 88 | h$a <- NULL # DELETE key 'a', will return null 89 | 90 | 91 | # INTERPRETED ACCESS 92 | 93 | h[[ "a" ]] <-"foo" # Assigns letters, a vector to "foo" 94 | nm = "a" 95 | 96 | # SLICE ACCESS 97 | h[ nm ] <- "bar" # h$a == bar 98 | h[ nm ] <- NULL 99 | 100 | 101 | # Slice 102 | h[ keys(h) ] 103 | h[ keys(h) ] <- list( 1:2, 1:3 ) 104 | h 105 | 106 | } 107 | \seealso{ 108 | \itemize{ 109 | \item \code{\link[=del]{del()}} for removing keys 110 | \item \code{\link[=clear]{clear()}} for removing all keys 111 | \item \code{\link[=keys]{keys()}} to get/set/rename keys 112 | \item \code{\link[=values]{values()}} to get/set/edit values 113 | \item \code{\link[=hash]{hash()}} 114 | } 115 | } 116 | \author{ 117 | Christopher Brown 118 | } 119 | -------------------------------------------------------------------------------- /man/format.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/format.R 3 | \docType{methods} 4 | \name{format} 5 | \alias{format} 6 | \alias{format,hash-method} 7 | \title{format - format a hash object for print} 8 | \usage{ 9 | \S4method{format}{hash}(x, max.print = getOption("max.print"), ...) 10 | } 11 | \arguments{ 12 | \item{x}{hash object} 13 | 14 | \item{max.print}{maximum numbers of rows to print} 15 | 16 | \item{...}{additional arguments} 17 | } 18 | \description{ 19 | Format a hash for printing/showing. 20 | } 21 | \seealso{ 22 | \code{\link[=format]{format()}} 23 | } 24 | -------------------------------------------------------------------------------- /man/has_key.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/has_key.R 3 | \docType{methods} 4 | \name{has_key} 5 | \alias{has_key} 6 | \alias{has.key} 7 | \alias{has.key,ANY,hash-method} 8 | \title{Test for existence of key(s) on a hash} 9 | \usage{ 10 | has_key(hash, key, ...) 11 | 12 | has.key(key, hash, ...) 13 | 14 | \S4method{has.key}{ANY,hash}(key, hash, ...) 15 | } 16 | \arguments{ 17 | \item{hash}{\strong{hash} object.} 18 | 19 | \item{key}{vector whose entries will be coerced to valid keys.} 20 | 21 | \item{...}{arguments passed to sapply.} 22 | } 23 | \value{ 24 | logical vector with `length(key)`` indicating whether 25 | the key is defined in the hash. 26 | } 27 | \description{ 28 | \strong{Note:} \code{has.key()} will be replaced by \code{has_key()} in the future. 29 | ` 30 | } 31 | \details{ 32 | \code{has_key} indicates whether the key exists on the hash. 33 | 34 | \code{has_key} is a wrapper around \code{\link[base:exists]{base::exists()}} vectorized over \code{key} using 35 | \code{\link[base:sapply]{base::sapply()}}. Additional arguments, \code{...} are passed to \code{\link[base:sapply]{base::sapply()}}. 36 | The inherits method is always \code{FALSE} 37 | 38 | \code{has_key()} is replacing \code{has.key} and switching the order of arguments. This 39 | makes \code{has_key()} more pipe-able and readable. \code{has.key()} will be deprecated 40 | in a future release. 41 | } 42 | \examples{ 43 | h <- hash( letters, 1:26 ) 44 | all( has.key( letters, h ) ) # TRUE 45 | 46 | } 47 | \seealso{ 48 | \itemize{ 49 | \item \code{\link[base:exists]{base::exists()}} 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /man/hash-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/Class-hash.R 3 | \docType{class} 4 | \name{hash-class} 5 | \alias{hash-class} 6 | \title{Class "hash"} 7 | \description{ 8 | Implements a S4 hash class in R similar to hashes / associatesd arrays / 9 | dictionaries in other programming languages. Where possible, the hash class 10 | uses the standard R accessors: \code{\\$}, \code{[} and \code{[[} as well as 11 | other common hash methods. Hash construction is flexible and takes several 12 | syntaxes. See the constructor method \code{\link[=hash]{hash()}} for detaiols 13 | } 14 | \details{ 15 | For shorter key-value pairs, lists yield higher performance. Hashes 16 | outperform list once the have an appreciable length typically greater than 17 | 100 elements. Hash objects have defined methods that (should) make them 18 | flexible, intuitive and easy to use especially for developers familar with 19 | other languages. 20 | 21 | The \code{haah} class inherits from environment and has no defined slots. It is 22 | essentially a wrapper around \code{\link[base:environment]{base::environment()}} and is very similar to 23 | reference classes in that most of the semantic are pass-by-reference rather 24 | than pass-by-value. For this reason, some of the behaviour is not as expected. 25 | } 26 | \section{Slots}{ 27 | 28 | \describe{ 29 | \item{\code{.xData}}{an \code{\link[base:environment]{base::environment()}}} 30 | }} 31 | 32 | \examples{ 33 | h <- new( "hash" ) # new empty hash 34 | h <- hash() # same 35 | class(h) # "hash" 36 | is.hash(h) # TRUE 37 | 38 | h[ letters ] <- 1:26 # populate the hash 39 | 40 | as.list( h ) # convert to a list 41 | 42 | hash( list(a=1,b=1) ) 43 | showClass("hash") 44 | 45 | } 46 | \seealso{ 47 | \itemize{ 48 | \item \link{extract} : for accessor methods 49 | \item \code{\link[=is.hash]{is.hash()}} : for testing whether the object is a hash 50 | \item \code{\link[=as.list.hash]{as.list.hash()}} : for converting hash to a list 51 | } 52 | } 53 | \author{ 54 | Christopher Brown 55 | } 56 | -------------------------------------------------------------------------------- /man/hash-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hash-package.R 3 | \docType{package} 4 | \name{hash-package} 5 | \alias{hash-package} 6 | \title{hash package} 7 | \description{ 8 | Hash/associative array/dictionary data structure for the R language. 9 | } 10 | \details{ 11 | This S4 class is designed to provide a hash-like data structure in a native 12 | R style and provides the general methods for hash/dictionary operations. 13 | } 14 | \note{ 15 | The hash package is the only full featured hash implementation for the R 16 | language. It provides more features and finer control of the hash behavior 17 | than the native features. It hss similar and some cases better 18 | performance, e.g. compared to lists with a large number of elements. 19 | } 20 | \examples{ 21 | 22 | h <- hash( keys=letters, values=1:26 ) 23 | h <- hash( letters, 1:26 ) 24 | 25 | h$a # 1 26 | 27 | h$foo <- "bar" 28 | h[ "foo" ] 29 | h[[ "foo" ]] 30 | 31 | clear(h) 32 | rm(h) 33 | 34 | } 35 | \references{ 36 | Some discussion can be found here: 37 | } 38 | \seealso{ 39 | \code{\link{hash} }, 40 | \code{\link[=Extract]{Extract()}} and 41 | \code{\link{environment} } 42 | } 43 | \author{ 44 | Christopher Brown 45 | 46 | Maintainer: Christopher Brown 47 | } 48 | -------------------------------------------------------------------------------- /man/hash.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hash.R, R/length.R 3 | \docType{methods} 4 | \name{hash} 5 | \alias{hash} 6 | \alias{is.hash} 7 | \alias{as.list.hash} 8 | \alias{length,hash-method} 9 | \alias{length} 10 | \title{hash/associative array/dictionary data structure for the R language} 11 | \usage{ 12 | hash(...) 13 | 14 | is.hash(x) 15 | 16 | \method{as.list}{hash}(x, all.names = FALSE, ...) 17 | 18 | \S4method{length}{hash}(x) 19 | } 20 | \arguments{ 21 | \item{...}{Additional arguments passed to the function 22 | #' HASH KEYS must be a valid character value and may not be the empty string 23 | \code{""}.} 24 | 25 | \item{x}{a hash object.} 26 | 27 | \item{all.names}{a logical indicating whether to copy all values or 28 | (default) only those whose names do not begin with a dot} 29 | } 30 | \value{ 31 | \code{hash} hash object 32 | 33 | \code{is.hash} logical value indicating if the argument is a hash. 34 | 35 | \code{as.list} list conversion from hash 36 | 37 | \code{length} integer; number of key-value pairs in the hash 38 | 39 | integer Number of items in the hash. 40 | } 41 | \description{ 42 | Functions for creating and working with hash objects: 43 | 44 | Returns the number of items in a hash 45 | } 46 | \details{ 47 | \code{hash} Class constructor 48 | 49 | \code{is.hash} test if object is of class "hash" 50 | 51 | \code{as.list} 52 | 53 | \code{as.list.hash} convert a hash object to a list 54 | 55 | \emph{KEYS} must be a valid R name, must be a character vector and must not 56 | be the empty string, \code{""}. When supplied by the used methods will try to 57 | coerce the keys to valid names using \code{\link[=make_keys]{make_keys()}} 58 | 59 | \emph{VALUES} are restricted to any valid R objects. 60 | HASH VALUES can be any R value, vector or object. 61 | 62 | \emph{code{hash}} returns a hash object. Key-value pairs may be specified 63 | as: \cr 64 | \itemize{ 65 | \item explicitly named arguments \code{keys} and \code{values} \cr 66 | \item two unnamed objects of equal length as a set of key-value pairs \cr 67 | \item key=value pairs \cr 68 | \item a single naned vector \cr 69 | \item as a list \cr 70 | } 71 | 72 | \emph{ACCESSORS.} Hashes may be accessed via the standard R accessors \code{[}, \code{[[} and 73 | \code{\\$}. See \code{\link[hash:extract]{hash::extract()}} for details. 74 | 75 | \emph{PASS-BY REFERENCE.} Environments and hashes are special objects in R because 76 | only one copy exists globally. When provided as an argument to a function, no 77 | local copy is made. When passes to functions, those functions can change the 78 | value of the hash. This is not typical of R. 79 | 80 | \emph{PERFORMANCE.} Hashes are based on R's native environments and are designed 81 | to be exceedingly fast using the environments internal hash table. For 82 | small data structures, a list will out-perform a hash in nearly every case. 83 | For larger data structure, i.e. > 500 key value pair the performance of the 84 | hash becomes faster. Much beyond that the performance of the hash far 85 | outperforms native lists. 86 | 87 | Return the number of items in the hash by calling \code{\link[=length]{length()}} on 88 | the internal environment. 89 | } 90 | \examples{ 91 | 92 | hash() # empty 93 | h <- hash() # associate a name to the hash 94 | 95 | hash( new.env() ) # from environment 96 | 97 | hash( a=1, b=2, c=3 ) # key-value pairs using named arguments 98 | 99 | hash( letters[1:3], 1:3 ) # unamed args: two equal length vectors 100 | hash( letters[1:3], 1 ) # unamed args: all keys with same value 101 | 102 | hash( 1:3, lapply(1:3, seq, 1 )) # same, arbitrary values 103 | 104 | hash( c(a=1, b=2, c=3) ) # named vector of key-value pairs 105 | hash( list(a=1,b=2,c=3) ) # named list of key-value pairs 106 | 107 | is.hash( hash() ) 108 | as.list(h) # CONVERT TO LIST 109 | 110 | 111 | 112 | h <- hash( letters, 1:26 ) 113 | length(h) # 26 114 | 115 | } 116 | \seealso{ 117 | \code{\link[=extract]{extract()}} 118 | 119 | See Also \code{\link[=hash]{hash()}}, \code{\link[=length]{length()}} 120 | } 121 | \author{ 122 | Christopher Brown 123 | 124 | Christpher Brown 125 | } 126 | \keyword{methods} 127 | -------------------------------------------------------------------------------- /man/head.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/head.R 3 | \name{head.hash} 4 | \alias{head.hash} 5 | \alias{tail.hash} 6 | \title{Head or tail of a hash} 7 | \usage{ 8 | \method{head}{hash}(x, ...) 9 | 10 | \method{tail}{hash}(x, ...) 11 | } 12 | \arguments{ 13 | \item{x}{hash;} 14 | 15 | \item{...}{Additional arguments passed to \code{\link[utils:head]{utils::head()}}} 16 | } 17 | \value{ 18 | A hash object display the first \code{n} key-value pairs. 19 | } 20 | \description{ 21 | There is no guaranteed order to the keys-values are produced. This may change 22 | in the futures. 23 | } 24 | \details{ 25 | Returns the first or last part of a hash 26 | } 27 | \examples{ 28 | 29 | h <- hash( letters, 1:26) 30 | head(h) 31 | tail(h) 32 | 33 | } 34 | \seealso{ 35 | \itemize{ 36 | \item \code{\link[utils:head]{utils::head()}} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /man/invert.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/invert.R 3 | \docType{methods} 4 | \name{invert} 5 | \alias{invert} 6 | \alias{invert,hash-method} 7 | \alias{inverted.hash} 8 | \title{Create/invert a hash.} 9 | \usage{ 10 | invert(x) 11 | 12 | \S4method{invert}{hash}(x) 13 | 14 | inverted.hash(...) 15 | } 16 | \arguments{ 17 | \item{x}{A \code{\link[=hash]{hash()}} object} 18 | 19 | \item{...}{Arguments passed to the \code{\link[=hash]{hash()}} function.} 20 | } 21 | \value{ 22 | A hash object with: keys as the unique elements of \code{values(x)} 23 | and values as the associated \code{keys{x}} 24 | } 25 | \description{ 26 | THIS IS AN EXPERIMENTAL FUNCTION. THE IMPLEMENTATION OR INTERFACE MAY CHANGE 27 | IN THE FUTURE. 28 | } 29 | \details{ 30 | \code{invert} exchanges the keys and values of a hash. 31 | 32 | \code{inverted.hash} is a constructor method that directly makes an inverted 33 | hash. 34 | 35 | Each element of the \code{values(x)} becomes a key in a new hash; the 36 | associatedis coerced to a key value is the 37 | The value becomes the associated key. 38 | 39 | For \code{inverted.hash}, a hash is created thnn inverted. It is defined 40 | as: 41 | 42 | \code{function(...) invert(hash(...))} 43 | } 44 | \examples{ 45 | 46 | h <- hash( a=1, b=1:2, c=1:3 ) 47 | invert(h) 48 | 49 | inverted.hash( a=1, b=1:2, c=1:3 ) 50 | 51 | } 52 | \seealso{ 53 | See also \code{link{hash}} and \code{\link[=make_keys]{make_keys()}} 54 | } 55 | \author{ 56 | Christopher Brown 57 | } 58 | -------------------------------------------------------------------------------- /man/is.empty.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/is.empty.R 3 | \name{is.empty} 4 | \alias{is.empty} 5 | \title{Test if a hash has no key-value pairs.} 6 | \usage{ 7 | is.empty(x) 8 | } 9 | \arguments{ 10 | \item{x}{hash object. 11 | 12 | Returns \code{TRUE} if no key-value pairs are defined for the hash, 13 | \code{FALSE} otherwise.} 14 | } 15 | \value{ 16 | logical. 17 | } 18 | \description{ 19 | \code{is.empty} tests to see if any key value pairs are assigned on a 20 | \code{hash} object. 21 | } 22 | \examples{ 23 | 24 | h <- hash( a=1, b=2, c=3 ) 25 | 26 | is.empty(h) # FALSE 27 | clear(h) 28 | is.empty(h) # TRUE 29 | h <- hash() 30 | is.empty(h) # TRUE 31 | 32 | } 33 | \seealso{ 34 | \code{\link[=hash]{hash()}} 35 | \code{\link[=exists]{exists()}} 36 | \code{\link[=length]{length()}} 37 | } 38 | \author{ 39 | Christopher Brown 40 | } 41 | -------------------------------------------------------------------------------- /man/keys.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keys.R 3 | \docType{methods} 4 | \name{keys} 5 | \alias{keys} 6 | \alias{keys-methods} 7 | \alias{keys,hash-method} 8 | \alias{names,hash-method} 9 | \alias{names} 10 | \alias{names.hash} 11 | \alias{keys<-} 12 | \alias{keys<--methods} 13 | \alias{keys<-,hash,ANY-method} 14 | \alias{keys<-,hash,NULL-method} 15 | \title{keys - get/set key(s) from a hash} 16 | \usage{ 17 | keys(x, ...) 18 | 19 | \S4method{keys}{hash}(x, sorted = getOption("hash.sorted", FALSE)) 20 | 21 | \S4method{names}{hash}(x) 22 | 23 | keys(x) <- value 24 | 25 | \S4method{keys}{hash,ANY}(x) <- value 26 | 27 | \S4method{keys}{hash,ANY}(x) <- value 28 | 29 | \S4method{keys}{hash,`NULL`}(x) <- value 30 | } 31 | \arguments{ 32 | \item{x}{A \code{\link[=hash]{hash()}} object.} 33 | 34 | \item{...}{Used to allow for additional arguments for keys} 35 | 36 | \item{sorted}{logical; whether the keys should be sorted 37 | (DEFAULT: \code{getOption('hash.sorted', FALSE)})} 38 | 39 | \item{value}{character or object coercable to character} 40 | } 41 | \value{ 42 | For \code{keys} and \code{names}, a character vector of key names 43 | For the replacement methods \code{keys<-}, a hash object with the keys 44 | renamed to \code{value} 45 | } 46 | \description{ 47 | Returns the key(s) from a hash, unsorted by default 48 | } 49 | \details{ 50 | Returns the character vector containing the keys of a hash object. By 51 | default, the responses are not sorted. Set \code{sorted=TRUE} to return 52 | keys in sort order. 53 | 54 | \code{names} uses \code{base::names} and will always return a sorted list of 55 | names. \code{keys} should generally be used in favor of \code{names}, but 56 | names can be useful if you want sorted names for one instance when all others 57 | don't provide a sorted list. 58 | } 59 | \examples{ 60 | 61 | h <- hash( letters, 1:26 ) 62 | keys(h) # letters 63 | 64 | names(h) # same 65 | 66 | #' Rename keys 67 | # keys( h ) <- rev( keys(h) ) 68 | 69 | } 70 | \seealso{ 71 | \itemize{ 72 | \item \code{\link[=setkeys]{setkeys()}} : rename specific hash keys. 73 | \item \code{\link[=hash]{hash()}} : hash object 74 | } 75 | } 76 | \author{ 77 | Christopher Brown 78 | } 79 | -------------------------------------------------------------------------------- /man/kv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/kv.R 3 | \name{kv} 4 | \alias{kv} 5 | \alias{kv.hash} 6 | \title{Key value iteration} 7 | \usage{ 8 | kv(x, ...) 9 | 10 | \method{kv}{hash}(x, ...) 11 | } 12 | \arguments{ 13 | \item{x}{object such as vector or list to separate into key value pairs} 14 | 15 | \item{...}{additional arguments} 16 | } 17 | \value{ 18 | A named list of list; each element of \code{x} becomes a one element list 19 | with elements \code{k} and \code{v} representing the keys and values 20 | } 21 | \description{ 22 | Create a list with \code{k} and \code{v} elements that is useful for 23 | explicit iteration 24 | } 25 | \details{ 26 | No magic here, just something simple to convert \code{x} into a list of 27 | lists. Each element of \code{x} is broken into a list with elements \code{k} 28 | (key) and \code{v} (value). See examples. 29 | 30 | For many cases, key-value iteration can be done with \code{*apply} or 31 | \code{paste} functions. This is made to be explicit and work on a variery of 32 | objects. 33 | } 34 | \examples{ 35 | 36 | h <- hash(a=1,b=2,c=3) 37 | kv(h) 38 | 39 | for( kv in kv(h) ) 40 | cat( kv$k, ":", kv$v, "\\n") 41 | 42 | } 43 | \references{ 44 | \itemize{ 45 | \item \url{http://stackoverflow.com/questions/18572921/for-loop-in-r-with-key-value]} \cr 46 | \item \url{http://stackoverflow.com/questions/4500106/iterate-over-key-value-pair-from-a-list]} \cr 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /man/make_keys.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_keys.R 3 | \name{make_keys} 4 | \alias{make_keys} 5 | \alias{make.keys} 6 | \title{creates/coerces objects to proper hash keys} 7 | \usage{ 8 | make_keys(key) 9 | 10 | make.keys(key) 11 | } 12 | \arguments{ 13 | \item{key}{An object that represents the key(s) to be coerced to a valid 14 | hash keys.} 15 | } 16 | \value{ 17 | A character vector of valid keys/names 18 | } 19 | \description{ 20 | \strong{Notes:} 21 | \itemize{ 22 | \item This function is not exported. 23 | \item \code{make.keys()} will be deprecated in a future release; Use \code{make_keys()} 24 | instead. 25 | } 26 | } 27 | \details{ 28 | Given a vector of any type, \code{make_keys} tries to coerce it into a 29 | character vector that can be used as a hash key. It is just a wrapper 30 | around \code{\link[base:as.character]{base::as.character()}} with some additional error checking. 31 | 32 | #' \code{make_keys} will replace \code{make.keys} in a future release. 33 | } 34 | \note{ 35 | This is used internally by the hash package and should not be normally 36 | needed. It is \emph{not} exported. 37 | } 38 | \examples{ 39 | 40 | \dontrun{ 41 | make_keys( letters ) 42 | make_keys( 1:26 ) 43 | } 44 | } 45 | \seealso{ 46 | \itemize{ 47 | \item \code{\link[base:as.character]{base::as.character()}} 48 | \item \code{\link[base:make.names]{base::make.names()}} 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /man/setkeys.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/setkeys.R 3 | \docType{methods} 4 | \name{setkeys} 5 | \alias{setkeys} 6 | \alias{setkeys,hash,ANY,ANY-method} 7 | \alias{setkeys,hash-method} 8 | \title{setkeys - change/rename the keys of a hash} 9 | \usage{ 10 | setkeys(x, old, new) 11 | 12 | \S4method{setkeys}{hash}(x, old, new) 13 | } 14 | \arguments{ 15 | \item{x}{hash object to rename keys on} 16 | 17 | \item{old}{character (or coerciable to character); old keys/names} 18 | 19 | \item{new}{character (or coerciable to character); new keys/names 20 | 21 | This methods renames keys in a hash. If there is a collision between \emph{old} 22 | and \emph{new} names, the old names are first copied to a temporary slot to 23 | ensure 24 | 25 | \code{setkeys} is S4 generic so that other packages might also use the 26 | generic functions} 27 | } 28 | \value{ 29 | Invisiblly returns \code{x} with its keys renamed. 30 | } 31 | \description{ 32 | Changes the keys of a hash from \code{old} keys to \code{new} 33 | } 34 | \note{ 35 | setkeys is modeled after \code{setnames} in the data.table package. 36 | } 37 | \examples{ 38 | 39 | h <- hash( letters, 1:26 ) 40 | 41 | h2 <- copy(h) 42 | setkeys( h2, keys(h), paste0( keys(h), "1" ) ) 43 | h2 44 | 45 | h3 <- copy(h) 46 | setkeys( h3, keys(h), rev( keys(h) ) ) 47 | h3 48 | 49 | } 50 | \seealso{ 51 | \itemize{ 52 | \item \code{\link[=keys]{keys()}} 53 | \item \code{\link[=hash]{hash()}} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /man/values.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/values.R 3 | \docType{methods} 4 | \name{values} 5 | \alias{values} 6 | \alias{values,hash-method} 7 | \alias{values<-} 8 | \alias{values<-,hash-method} 9 | \title{values} 10 | \usage{ 11 | values(x, ...) 12 | 13 | \S4method{values}{hash}(x, keys = NULL, ...) 14 | 15 | values(x, ...) <- value 16 | 17 | \S4method{values}{hash}(x, ...) <- value 18 | } 19 | \arguments{ 20 | \item{x}{hash object} 21 | 22 | \item{...}{Unused. 23 | 24 | The \code{values} method returns a named-list of key value pairs from a hash. 25 | If a hash is desired, use the hash slice method, \code{h[x]}. 26 | 27 | List elements are named after the corresponding key. In previous versions, 28 | the return was simplified. This is no longer the case. Users should 29 | simplify arguments if needed using \code{unlist} or similar. See examples. 30 | 31 | If argument \code{keys} is provided, only these keys are 32 | returned. This also allows for returning values mulitple times as in: 33 | 34 | \code{values(h, keys=c( 'a','a','b' ) )} 35 | 36 | This is now the preferred method for returning multiple values for the same 37 | key. 38 | 39 | The replacement method, \code{values<-} can replace all the values or simply 40 | those associated with the supplied \code{keys}. Use of the accessor '[' is 41 | almost always preferred.} 42 | 43 | \item{keys}{character; names of keys to get} 44 | 45 | \item{value}{to be set} 46 | } 47 | \value{ 48 | A named list. Names are those of the keys, values are the associated hash 49 | values. 50 | } 51 | \description{ 52 | Get/set values for a hash object 53 | } 54 | \examples{ 55 | h <- hash( letters, 1:26 ) 56 | values(h) # 1:26 57 | 58 | h <- hash( 1:26, letters ) 59 | values(h) 60 | values(h, keys=1:5 ) 61 | values(h, keys=c(1,1,1:5) ) 62 | 63 | values(h, keys=1:5) <- 6:10 64 | values(h) <- rev( letters ) 65 | 66 | # When values are obejcts 67 | h <- hash( c('a','b'), Sys.time() ) 68 | class(h$a) # "POSIXct" "POSIXt" 69 | vals <- values( h ) 70 | class( unlist(vals) ) # numeric 71 | class( Reduce( c, vals ) ) # "POSIXct" "POSIXt" 72 | 73 | vals <- values(h) 74 | class(vals) # List 75 | class( Reduce( c, vals ) ) # "POSIXct" "POSIXt" 76 | 77 | } 78 | \seealso{ 79 | \itemize{ 80 | \item \link{extract} for R-like accessors 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | 3 | `%>%` <- function( lhs, rhs ) { 4 | 5 | lr <- lazy(rhs) 6 | r <- substitute(rhs) 7 | 8 | if( is.call(r) ) { 9 | lst_r <- as.list(r) 10 | do.call( as.character(lst_r[[1]]), c( substitute(lhs), lst_r[-1] ) ) 11 | } else { 12 | do.call( eval(r), list(lhs)) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tests/testthat/test-accessors.r: -------------------------------------------------------------------------------- 1 | # library(hash) 2 | # library(testthat) 3 | 4 | context('accessors') 5 | 6 | h0 <- hash() 7 | h <- hash( letters, 1:26 ) 8 | 9 | # OBJECT CREATION / TYPE 10 | test_that( "OBJECT CREATION / TYPE", { 11 | h %>% expect_is("hash") 12 | h0 %>% expect_is("hash") 13 | }) 14 | 15 | 16 | # EMPTY HASH 17 | test_that( "EMPTY HASH", { 18 | h0 %>% expect_is('hash') 19 | length(h0) %>% expect_equal(0) 20 | }) 21 | 22 | # POPULATED HASH 23 | test_that( "POPULATED HASH", { 24 | h %>% expect_is('hash') 25 | h %>% length %>% expect_equal(26) 26 | h %>% keys %>% sort %>% expect_identical(letters) 27 | }) 28 | 29 | 30 | # ALL HASHES 31 | test_that( "ALL HASHES", { 32 | for( h in list( h0, h) ) { 33 | h[['missing']] %>% expect_identical(NULL, label = "Attempt to retrieve missing key" ) 34 | } 35 | }) 36 | 37 | # TEST [[ 38 | test_that( "[[", { 39 | for( h in list( h0, h ) ) { 40 | expect_error( h[[NULL]] ) 41 | expect_error( h[[NA]] ) 42 | expect_error( h[[]] ) 43 | expect_error( h[[letters]] ) 44 | } 45 | 46 | for( n in 1:26 ) 47 | h[[ letters[n] ]] %>% expect_identical(n) 48 | }) 49 | 50 | 51 | # TEST $ 52 | test_that("$", { 53 | h$a %>% expect_equal(1) 54 | h$z %>% expect_equal(26) 55 | }) 56 | 57 | # TEST [ 58 | test_that( "[", { 59 | for( h in list( h0, h ) ) { 60 | expect_error( h[[NULL]] ) 61 | expect_error( h[[NA]] ) 62 | expect_error( h[[]] ) 63 | expect_error( h[[letters]] ) 64 | } 65 | 66 | h[letters] %>% expect_equivalent(h) 67 | }) 68 | 69 | # TEST [[ <- 70 | test_that( "[[<-", { 71 | h[['a']] <- -1 72 | expect_that( h[['a']], equals(-1) ) 73 | }) 74 | 75 | # TEST $<- 76 | test_that( "$<-", { 77 | h$b <- -2 78 | expect_that( h$b, equals(-2) ) 79 | }) 80 | -------------------------------------------------------------------------------- /tests/testthat/test-as.list.r: -------------------------------------------------------------------------------- 1 | library(hash) 2 | library(testthat) 3 | 4 | 5 | context('as.list') 6 | test_that( 'as.list', { 7 | h <- hash( a=1, b=2, c=3 ) 8 | li <- as.list( h ) 9 | expect_is( li, 'list' ) 10 | expect_true( length(li) == 3 ) 11 | expect_equal( names(li), letters[1:3] ) 12 | expect_equivalent( unlist(li), 1:3 ) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/test-clear.r: -------------------------------------------------------------------------------- 1 | # test-clear.r 2 | 3 | test_that( "clear", { 4 | 5 | context('clear') 6 | 7 | # if( exists('h') ) rm(h) 8 | 9 | h <- hash( letters, 1 ) 10 | 11 | h %>% expect_is("hash") 12 | h %>% length %>% expect_equal(26) 13 | 14 | clear(h) 15 | h %>% length %>% expect_equal(0) 16 | 17 | rm(h) 18 | exists('h') %>% expect_false() 19 | 20 | }) 21 | -------------------------------------------------------------------------------- /tests/testthat/test-copy.r: -------------------------------------------------------------------------------- 1 | context( "copy") 2 | 3 | context( '.. basic' ) 4 | test_that( 'copy', { 5 | 6 | h <- hash( letters[1:3], 1:3 ) 7 | h_copy <- copy(h) 8 | 9 | h_copy %>% expect_is('hash') 10 | h_copy %>% expect_equal(h) 11 | 12 | if( require(pryr) ) 13 | expect_true( pryr::address(h) != pryr::address(h_copy)) 14 | 15 | }) 16 | 17 | context( '.. empty hash [#12]') 18 | test_that( 'empty hash [#12]', { 19 | 20 | h <- hash() 21 | h_copy <- copy(h) 22 | 23 | h_copy %>% expect_is('hash') 24 | h_copy %>% length %>% expect_equal(0) 25 | h_copy %>% expect_equal(h) 26 | 27 | if( require(pryr) ) 28 | expect_true( pryr::address(h) != pryr::address(h_copy)) 29 | 30 | }) 31 | 32 | context( '.. single key-value pair [#14]') 33 | test_that( 'single key-value pair [#14]', { 34 | 35 | h <- hash( a=1 ) 36 | h_copy <- copy(h) 37 | 38 | h_copy %>% expect_is('hash') 39 | h_copy %>% length %>% expect_equal(1) 40 | expect_equivalent(h_copy, h) 41 | 42 | if( require(pryr) ) 43 | expect_true( pryr::address(h) != pryr::address(h_copy)) 44 | 45 | }) -------------------------------------------------------------------------------- /tests/testthat/test-has.key.r: -------------------------------------------------------------------------------- 1 | library(hash) 2 | library(testthat) 3 | 4 | context('has.key') 5 | 6 | test_that("has.key", { 7 | #' Ensure that there are no keys on emptyhash 8 | h <- hash() 9 | 'c' %>% has.key(h) %>% expect_false() 10 | 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-hash.r: -------------------------------------------------------------------------------- 1 | library(hash) 2 | library(testthat) 3 | 4 | context('constructor methods') 5 | 6 | context('.. empty hash') 7 | test_that( 'empty hash', { 8 | hash() %>% expect_is('hash') # hash constructor 9 | hash() %>% length %>% expect_equal(0) 10 | 11 | h <- hash() 12 | h %>% expect_is('hash') 13 | h %>% is.hash %>% expect_true() 14 | h %>% length %>% expect_equal(0) 15 | h %>% keys %>% length %>% expect_equal(0) 16 | h %>% values %>% length %>% expect_equal(0) 17 | }) 18 | 19 | context('.. from environment') 20 | test_that( 'from environment', { 21 | h <- hash( new.env() ) # from environment 22 | h %>% expect_is('hash') 23 | h %>% is.hash %>% expect_true() 24 | h %>% length %>% expect_equal(0) 25 | h %>% keys %>% length %>% expect_equal(0) 26 | h %>% values %>% length %>% expect_equal(0) 27 | }) 28 | 29 | context('.. from list') 30 | test_that( 'from list', { 31 | h <- hash( list() ) # from environment 32 | h %>% expect_is('hash') 33 | h %>% is.hash %>% expect_true() 34 | h %>% length %>% expect_equal(0) 35 | h %>% keys %>% length %>% expect_equal(0) 36 | h %>% values %>% length %>% expect_equal(0) 37 | }) 38 | 39 | 40 | context('.. from key-value pairs') 41 | test_that( 'from key-value pairs', { 42 | h <- hash( a=1, b=2, c=3 ) # Set hash from key-value pairs 43 | h %>% expect_is('hash') 44 | h %>% is.hash %>% expect_true() 45 | h %>% length %>% expect_equal(3) 46 | h %>% keys %>% length %>% expect_equal(3) 47 | h %>% values %>% length %>% expect_equal(3) 48 | h %>% values %>% expect_equivalent(1:3) 49 | h %>% keys %>% expect_equal( letters[1:3] ) 50 | h %>% names %>% expect_equal( letters[1:3] ) 51 | }) 52 | 53 | context('.. from unnamed args, equal-length vectors') 54 | test_that( 'from unnamed args, equal-length vectors', { 55 | h <- hash( letters[1:3], 1:3 ) # unanmed args: two equal length vectors 56 | h %>% expect_is('hash') 57 | h %>% is.hash %>% expect_true() 58 | h %>% length %>% expect_equal(3) 59 | h %>% keys %>% length %>% expect_equal(3) 60 | h %>% values %>% length %>% expect_equal(3) 61 | h %>% values %>% expect_equivalent(1:3) 62 | h %>% names %>% expect_equal( letters[1:3] ) 63 | }) 64 | 65 | context('.. from unamed, all values the same') 66 | test_that( 'from unamed, all values the same', { 67 | h <- hash( letters[1:3], 1 ) # unamed args: all values thes same value 68 | h %>% expect_is('hash') 69 | h %>% is.hash %>% expect_true() 70 | h %>% length %>% expect_equal(3) 71 | h %>% keys %>% length %>% expect_equal(3) 72 | h %>% values %>% length %>% expect_equal(3) 73 | h %>% values %>% expect_equivalent( rep(1,3) ) 74 | h %>% names %>% expect_equal( letters[1:3] ) 75 | }) 76 | 77 | context('.. character & list') 78 | test_that( 'character & list', { 79 | h <- hash( letters[1:3], lapply(1:3, seq, 1 )) # same, complex values 80 | h %>% expect_is('hash') 81 | h %>% is.hash %>% expect_true() 82 | h %>% length %>% expect_equal(3) 83 | h %>% keys %>% length %>% expect_equal(3) 84 | h %>% values %>% length %>% expect_equal(3) 85 | 86 | expect_equal( keys(h), letters[1:3] ) 87 | expect_is( values(h), 'list' ) 88 | expect_equivalent( unlist(values(h)) , c(1,2,1,3,2,1) ) 89 | expect_equal( names(values(h)), letters[1:3] ) 90 | 91 | 92 | h <- hash( c(a=1, b=2, c=3) ) # named vector of key-value pairs 93 | expect_is( h , 'hash' ) 94 | expect_true( is.hash(h) ) 95 | expect_equal( length(h), 3) 96 | expect_equal( length(keys(h)), 3) 97 | expect_equal( length(values(h)), 3) 98 | expect_equal( keys(h), letters[1:3] ) 99 | expect_equivalent( values(h), 1:3 ) 100 | expect_equal( names(values(h)), letters[1:3] ) 101 | 102 | 103 | h <- hash( list(a=1,b=2,c=3) ) # named list of key-value pairs 104 | expect_is( h , 'hash' ) 105 | expect_true( is.hash(h) ) 106 | expect_equal( length(h), 3) 107 | expect_equal( length(keys(h)), 3) 108 | expect_equal( length(values(h)), 3) 109 | expect_equal( keys(h, sorted=TRUE), letters[1:3] ) 110 | expect_equivalent( values(h) %>% unlist %>% sort, 1:3 ) 111 | expect_equal( names(values(h)) %>% unlist %>% sort, letters[1:3] ) 112 | }) 113 | 114 | -------------------------------------------------------------------------------- /tests/testthat/test-issues.r: -------------------------------------------------------------------------------- 1 | 2 | context("Isues") 3 | test_that( '#6', { 4 | 5 | context(' #6') 6 | tm <- Sys.time() 7 | h <- hash() 8 | h$tm <- tm 9 | 10 | expect_equal( class(h$tm), class(tm) ) 11 | expect_equal( h$tm, tm ) 12 | 13 | }) 14 | -------------------------------------------------------------------------------- /tests/testthat/test-set.r: -------------------------------------------------------------------------------- 1 | library(hash) 2 | library(testthat) 3 | 4 | # TEST FILE FOR hash METHODS set 5 | 6 | context( '.set') 7 | 8 | h <- hash() 9 | 10 | test_that( ".set", { 11 | 12 | # SET: key-value pairs 13 | .set( h, "a", 1:2 ) 14 | expect_equal( h[["a"]], 1:2 ) 15 | 16 | .set( h, letters, 1:26 ) 17 | expect_equal( h[["a"]], 1 ) 18 | 19 | .set( h, 1:5, 1:5 ) 20 | expect_equal( h[["1"]], 1 ) 21 | 22 | .set( h, letters, 12 ) 23 | expect_equal( h[["a"]], 12 ) 24 | 25 | # SET: key-hash pair added in version 1.0.4 26 | .set( h, "ha", hash( a=1, b=2 ) ) 27 | class( h[["ha"]] ) == "hash" 28 | 29 | 30 | 31 | # SET: data.frame 32 | .set( h, "df", data.frame( a=1:10, b=11:20) ) 33 | class( h[["df"]] ) == "data.frame" 34 | 35 | # SET: list 36 | .set( h, "li", list( a=1, b=1:5, c=letters[1:3] ) ) 37 | class( h[["li"]] ) == "list" 38 | 39 | # SET: environment 40 | }) 41 | -------------------------------------------------------------------------------- /vignettes/benchmarks.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Benchmarks" 3 | author: "Christopher Brown" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Vignette Title} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | 13 | 14 | ```{r, echo=FALSE, warning=FALSE, error=FALSE, results='hide', message=FALSE} 15 | library(hash) 16 | 17 | library(magrittr) 18 | library(knitr) 19 | library(ggplot2) 20 | library(microbenchmark) 21 | library(scales) 22 | 23 | replications <- 10 24 | ``` 25 | 26 | ## Environment 27 | 28 | The hash version: `r packageVersion('hash')`. 29 | 30 | 31 | ### System 32 | 33 | ```{r, echo=FALSE} 34 | R.version %>% unlist() %>% as.data.frame() %>% kable() 35 | 36 | ``` 37 | 38 | 39 | ## BENCHMARK: Accessing Keys 40 | 41 | Read keys from an 1,000 element hash, optionally sorting. 42 | 43 | ```{r benchmark0, cache=TRUE, echo=FALSE} 44 | h <- hash( 1:1000, rnorm(1000) ) 45 | 46 | microbenchmark( 47 | keys.sorted = keys(h,sorted=TRUE) 48 | , keys.unsorted = keys(h,sorted=FALSE) 49 | , names = names( h@.xData ) 50 | , replication = replications 51 | ) %>% summary %>% kable 52 | 53 | ``` 54 | 55 | 56 | ## BENCHMARK 1: Assigining Values to Empty Environments 57 | 58 | 59 | ```{r, cache=TRUE, echo=FALSE } 60 | 61 | size <- 5e4 # The size of the refernece objects. 62 | keys <- as.character( sample(1:size) ) # A vector of keys 63 | values <- as.character( rnorm( size ) ) 64 | 65 | lst <- list() 66 | 67 | env.mapply <- new.env( hash = T , parent = emptyenv() ) 68 | env.lapply <- new.env( hash = T , parent = emptyenv() ) 69 | env.for <- new.env( hash = T , parent = emptyenv() ) 70 | h <- hash() 71 | ``` 72 | 73 | This benchmark compares methods for assigning multiple values to an empty/growing environment iterating with `for`, `lapply` or `mapply`. Perhaps somewhat surprisingly, The `for` loop is the fastest method by a considerable margin. This is used internally for setting kv pairs. 74 | 75 | 76 | ```{r benchmark1, echo=FALSE, cache=TRUE } 77 | times = 5 78 | microbenchmark( 79 | for_loop = for( i in 1:length(keys) ) assign( keys[[i]], values[[i]], envir = env.for ) , 80 | mapply = mapply( assign, keys, values, MoreArgs = list( envir = env.mapply ) ) , 81 | lapply = lapply( 82 | ( 1:length(keys) ) , 83 | FUN = function(i) assign( keys[[i]], values[[i]], envir = env.lapply ) 84 | ) , 85 | times = times, 86 | unit = 'ms' 87 | ) %>% summary %>% kable 88 | 89 | 90 | ``` 91 | 92 | 93 | ## BENCHMARK 2: Assigning Values to Existing 94 | 95 | This benchmarks comapres the time to assign values to existing structures of various sized. 96 | 97 | 98 | ```{r benchmark2, cache=TRUE, echo=FALSE, fig.width=6} 99 | n.writes <- 1000 100 | sizes = 2^(5:13) # Elements in object 101 | 102 | bm2 <- data.frame() 103 | if( exists('res') ) rm(res) 104 | 105 | for( size in sizes ) { 106 | 107 | # CREATE NAMED-LIST: 108 | li<-mapply( 109 | function(k,v) { 110 | li<-list() 111 | li[[k]]<-v 112 | li 113 | } , 114 | keys[1:size] , 115 | values[1:size] , 116 | USE.NAMES=F 117 | ) 118 | 119 | 120 | # CREATE NAMED-HASH: 121 | ha <- hash( keys[1:size], values[1:size] ) 122 | 123 | # CREATE ENV 124 | en <- new.env( hash=TRUE ) 125 | for( i in 1:size ) assign( keys[[i]], values[[i]], en ) 126 | 127 | # CREATE A VECTOR 128 | ve <- values[1:size] 129 | names(ve) <- keys[1:size] 130 | 131 | # CREATE KEYS TO LOOK UP: 132 | # kes <- keys[ round(runif(n=n.writes,min=1,max=length(keys) )) ] 133 | # ke <- keys[ round(runif(max=size,min=1,n=slice.size )) ] 134 | 135 | sample( letters, 1 ) 136 | ke <- sample(keys,1 ) 137 | 138 | res <- 139 | microbenchmark( 140 | `hash` = ha[[ke]] <- "a" , 141 | `list` = li[[ke]] <- "a" , 142 | `vector` = ve[[ke]] <- "a" , 143 | `env/assign` = assign( ke, "a" , en ) , 144 | unit = "us", 145 | times = n.writes 146 | ) %>% summary 147 | 148 | res$size <- size 149 | bm2<- if( nrow(bm2)==0) res else rbind( bm2, res ) 150 | 151 | } 152 | 153 | 154 | gg <-ggplot( bm2, aes(x=size, y=median, color=expr ) ) 155 | 156 | gg + 157 | geom_point() + geom_line() + geom_point( size=0.5, color="white") + 158 | scale_x_log10("Object Size (# Elements)", labels=comma, breaks=sizes ) + 159 | scale_y_continuous( "Median Time (microsecond)" ) 160 | 161 | 162 | 163 | 164 | ``` 165 | 166 | 167 | ## BENCHMARK 3: Accessing/Reading Single Values 168 | 169 | This benchmark looks up single value in objects of increasing sizes. We can note 170 | two trends: 171 | 172 | 1. For objects of with elements < 500-1000 elements, using a native lists or 173 | factors is much faster than using a hash. Above this, number hashes far 174 | outperform vectors and lists. 175 | 176 | 2. The fastest retrieval method is `get` directly on the environment. This can 177 | be done with hashes. 178 | 179 | 180 | ```{r benchmark3, echo=FALSE, cache=TRUE, fig.width=6} 181 | times = 100 182 | number.of.lookups <- 1e3 183 | bm3 <- data.frame() 184 | if( exists('res') ) rm(res) 185 | # LOOP OVER SIX ORDERS OF MAGNITUDES. 186 | for( size in 2^(5:13) ) { 187 | 188 | # CREATE NAMED-LIST: 189 | li<-mapply( 190 | function(k,v) { 191 | li<-list() 192 | li[[k]]<-v 193 | li 194 | } , 195 | keys[1:size] , 196 | values[1:size] , 197 | USE.NAMES=F 198 | ) 199 | 200 | 201 | # CREATE NAMED-HASH: 202 | ha <- hash( keys[1:size], values[1:size] ) 203 | 204 | # CREATE A VECTOR 205 | ve <- values[1:size] 206 | names(ve) <- keys[1:size] 207 | 208 | 209 | # CREATE KEYS TO LOOK UP: 210 | ke <- keys[ round(runif(max=size,min=1,n=number.of.lookups )) ] 211 | k <- sample( keys[1:size], 1 ) 212 | 213 | res <- microbenchmark( 214 | `get/env` = get( k, ha@.xData ) , 215 | `get/hash` = get( k, ha ) , 216 | `mget/env` = mget(k, ha@.xData, ifnotfound = list(NULL) ) , 217 | `hash[[k]]` = ha[[k]] , 218 | `list[[k]]` = li[[k]] , 219 | `vector[[k]]` = ve[[k]] , 220 | times = times, 221 | unit = "us" 222 | ) %>% summary 223 | 224 | res$size <- size 225 | bm3 <- rbind( bm3, res ) 226 | 227 | } 228 | 229 | 230 | gg <- 231 | ggplot(bm3 , aes(x=size, y=jitter(median), color=expr )) 232 | 233 | gg + geom_point() + geom_line() + geom_point( size=0.5, color="white") + 234 | scale_x_log10("Object Size (# Elements)", labels=comma, breaks=sizes, minor_breaks=waiver() ) + 235 | scale_y_continuous( "Median Time (microsecond)", labels=comma ) 236 | 237 | 238 | 239 | 240 | 241 | ``` 242 | 243 | 244 | ## BENCHMARK 4: Reading Multiple Values / Slices (`[`) 245 | 246 | Take slices of an object. 247 | 248 | ```{r benchmark4, echo=FALSE, fig.width=6 } 249 | times = 100 250 | slice.pct <- 0.01 251 | n.lookups <- 100 252 | bm4 <- data.frame() 253 | 254 | for( size in 2^(5:13) ) { 255 | 256 | slice.size <- floor( size * slice.pct ) + 1 257 | # cat( "\nComparing slice time for object of size", size, "with slice pct", slice.pct, "\n" ) 258 | 259 | # CREATE NAMED-LIST: 260 | li<-mapply( 261 | function(k,v) { 262 | li<-list() 263 | li[[k]]<-v 264 | li 265 | } , 266 | keys[1:size] , 267 | values[1:size] , 268 | USE.NAMES=F 269 | ) 270 | 271 | 272 | # CREATE NAMED-HASH: 273 | ha <- hash( keys[1:size], values[1:size] ) 274 | 275 | # CREATE A VECTOR 276 | ve <- values[1:size] 277 | names(ve) <- keys[1:size] 278 | 279 | # CREATE KEYS TO LOOK UP: 280 | # kes <- lapply( 1:n.lookups, function(x) keys[ round(runif(max=size,min=1,n=slice.size )) ] ) 281 | # ke <- keys[ round(runif(max=size,min=1,n=slice.size )) ] 282 | 283 | ke = sample(keys[1:size], min(size,100) ) 284 | 285 | res <- 286 | microbenchmark( 287 | `hash` = ha[ ke ] , 288 | # `list` = li[ ke ] , 289 | `vector` = ve[ ke ] , 290 | `mget/env` = mget( ke, ha@.Data ) , 291 | times = times , 292 | unit = "us" 293 | 294 | ) %>% summary 295 | 296 | res$size <- size 297 | bm4 <- if( nrow(bm4)==0) res else rbind( bm4, res ) 298 | 299 | } 300 | gg <- 301 | ggplot(bm4 , aes(x=size, y=jitter(median), color=expr )) 302 | 303 | gg + geom_point() + geom_line() + geom_point( size=0.5, color="white") + 304 | scale_x_log10("Object Size (# Elements)", labels=comma, breaks=sizes, minor_breaks=waiver() ) + 305 | scale_y_continuous( "Median Time (microsecond)", labels=comma ) 306 | 307 | 308 | ``` 309 | -------------------------------------------------------------------------------- /vignettes/using-hash.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using hash" 3 | author: "Christopher Brown" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Using hash} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | 13 | The *hash* package provides a fully-functional hash/dictionary for R. It has 14 | richer features and finer control than using native R structures like lists or 15 | environments and has as a user-friendly interface. 16 | 17 | Performance-wise it has similar and sometimes better performance than these 18 | structures. 19 | 20 | 21 | ## Installation 22 | 23 | Latest Release: 24 | 25 | install.packages('hash') 26 | 27 | 28 | Development Version: 29 | 30 | install.packages('devtools') 31 | devtools::install_github('decisionpatterns/hash') 32 | 33 | 34 | ## Examples 35 | 36 | # Create a hash 37 | h <- hash(a=1, b=2, c=3) 38 | h <- hash( letters[1:3], 1:3 ) 39 | h <- hash( list(a=1,b=2,c=3) ) 40 | 41 | # Keys 42 | keys(h) 43 | 44 | # Values (named list) 45 | values(h) 46 | 47 | # Assign to single key hash 48 | h$a <- "foo" 49 | h[['a']] <- "bar" 50 | 51 | # Slice 52 | h[ c('a','c') ] 53 | 54 | ## Accessors 55 | 56 | ## NAMED ACCESS/REPLACEMENT: 57 | 58 | h$x : returns value of key `x`; 59 | h$x <- value : sets key `x` to value; 60 | h$x <- NULL : deletes key-value pair `x` 61 | 62 | 63 | ## INTERPRETED ACCES/REPLACEMENT: 64 | 65 | h[[x]] : returns value of key x; `x` is interpreted. 66 | h[[x]] <- value : sets the values of key `x`; `x` is interpreted. 67 | h[[x]] <- NULL : deletes key-value pair `x`; `x` is interpreted. 68 | 69 | 70 | ## HASH SLICING: 71 | 72 | h[] : returns a copy of h, same as `copy(h)` 73 | h[x] : a hash slice of keys 74 | 75 | h[] <- value : error, undefined key 76 | h[x] <- value : set values for keys `x` to `value`(s) 77 | h[x] <- NULL : delete keys `x` 78 | 79 | 80 | 81 | ## Allowable Values 82 | 83 | 84 | **KEYS** must be a valid character value and may not be the empty string (""). Keys must be unique. 85 | 86 | **VALUES** can be any R value, vector, object, etc. 87 | 88 | 89 | ## Usage Notes 90 | 91 | Hashes probably work about how you would expect, but since there are built from R's native environments. There are three things to Remember: 92 | 93 | **PASS-BY REFERENCE**. hashes are environments, special objects in R where only one copy exists globally. When passed as an argument to a function, no local copy is made and any changes to the hash in the functions are reflected globally, i.e. in the caller's namespace. 94 | 95 | **PERFORMANCE**. Hashes are designed to be exceedingly fast using R environment's internal hash table. The hash function is not without its cost. For small data structures, a named lists and vectors will out-perform a hash in nearly every case. After approximately 500+ elements, the performance of the hash becomes faster than native lists and vectors. 96 | --------------------------------------------------------------------------------