# Introduction

# As I explored the concept of hashmaps, I learned how to implement them using R's environment objects. This journey into environments as hashmaps showed me how to efficiently manage key-value pairs through hashed key lookups. I found environments to be flexible and capable of storing various data types, offering a powerful alternative for efficient data retrieval and storage.

# Creating a Hashmap with Environments

# I began by creating a new environment, which automatically provides hashed key lookups. The `new.env()` function is central to this process. By default, the `hash` parameter is set to TRUE.
H <- new.env()
# I also explored pre-allocating the internal hash table size using the `size` parameter. Although optional, setting a non-default value can optimize performance for large datasets.
object.size(new.env())
## 56 bytes
object.size(new.env(size = 10e4))
## 56 bytes
# Insertion of Elements

# Inserting elements into the environment was straightforward. I used the `[[<-` operator for adding key-value pairs. Here are a few examples of inserting different types of data:
H[["key1"]] <- rnorm(1)
key2 <- "xyz"
H[[key2]] <- data.frame(x = 1:3, y = letters[1:3])
H$another_key <- matrix(rbinom(9, 1, 0.5) > 0, nrow = 3)
# I learned that single bracket assignment (`[<-`) is not supported for environments:

# H["error"] <- 42 # This would raise an error.

# I observed that updating an existing key replaced the value:
H[["key3"]] <- "original value"
H[["key3"]] <- "new value"
# Key Lookup

# Retrieving values from the environment was as easy as inserting them. I used the `[[` and `$` operators to access stored elements:
retrieved_value <- H[["key1"]]
data_frame_value <- H[[key2]]
matrix_value <- H$another_key
# Inspecting the Hashmap

# To inspect the contents of the hashmap, I utilized functions like `names()`, `ls()`, and `str()`:

names(H)
## [1] "another_key" "xyz"         "key1"        "key3"
ls(H)
## [1] "another_key" "key1"        "key3"        "xyz"
str(H)
## <environment: 0x0000018aa6485d48>
ls.str(H)
## another_key :  logi [1:3, 1:3] FALSE FALSE FALSE FALSE TRUE FALSE ...
## key1 :  num -1.2
## key3 :  chr "new value"
## xyz : 'data.frame':  3 obs. of  2 variables:
##  $ x: int  1 2 3
##  $ y: chr  "a" "b" "c"
# Removing elements was just as straightforward using the `rm()` function:

rm(list = c("key1", "key3"), envir = H)
# Flexibility of Environments

# One of the most significant advantages of using environments as hashmaps is their flexibility. I could store various data types, even nested environments:
H2 <- new.env()
H2[["a"]] <- LETTERS
H2[["b"]] <- as.list(x = 1:5, y = matrix(rnorm(10), 2))
H2[["c"]] <- head(mtcars, 3)
H2[["d"]] <- Sys.Date()
H2[["e"]] <- Sys.time()
# Limitations and Vectorization

# Despite the flexibility, environments have limitations. One major constraint is the lack of support for vectorized element lookup or insertion:

# names(H2)
# H2[[c("a", "b")]] # This would raise an error.

# Using `vapply` and `list2env` can help manage multiple key-value assignments:
E1 <- new.env()
invisible({
  vapply(letters, function(x) {
    E1[[x]] <- rnorm(1)
    logical(0)
  }, FUN.VALUE = logical(0))
})

E2 <- list2env(
  setNames(
    as.list(rnorm(26)),
    nm = letters
  ),
  envir = NULL,
  hash = TRUE
)
# Load necessary libraries
library(knitr)
library(kableExtra)

# Create the data frame
data <- data.frame(
  Key = c("key1", "key2", "another_key", "key3"),
  `Value Type` = c("Numeric", "Data Frame", "Matrix", "Character"),
  `Example Value` = c(
    "Random Normal Value",
    "data.frame(x = 1:3, y = letters[1:3])",
    "matrix(rbinom(9, 1, 0.5) > 0, nrow = 3)",
    "'new value'"
  )
)

# Generate the colorful table
kable(data, "html") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#4d84c1") %>%
  row_spec(1, background = "#FFCCCB") %>%
  row_spec(2, background = "#D5F5E3") %>%
  row_spec(3, background = "#AED6F1") %>%
  row_spec(4, background = "#FFCCCB")
Key Value.Type Example.Value
key1 Numeric Random Normal Value
key2 Data Frame data.frame(x = 1:3, y = letters[1:3])
another_key Matrix matrix(rbinom(9, 1, 0.5) > 0, nrow = 3)
key3 Character ‘new value’
# Conclusion

# This exploration of hashmaps using R's environment objects highlighted their efficiency and flexibility. By leveraging environments, I gained insights into managing key-value pairs effectively, enhancing my data manipulation skills in R.