kandi background
Explore Kits

rivr | lightweight open-source dialogue engine enabling Java | Build Tool library

 by   nuecho Java Version: v1.0.11 License: Non-SPDX

 by   nuecho Java Version: v1.0.11 License: Non-SPDX

Download this library from

kandi X-RAY | rivr Summary

rivr is a Java library typically used in Utilities, Build Tool, Gradle, Maven applications. rivr has no bugs, it has no vulnerabilities, it has build file available and it has low support. However rivr has a Non-SPDX License. You can download it from GitHub, Maven.
Rivr is a lightweight open-source dialogue engine enabling Java developers to easily create enterprise-grade VoiceXML applications. Read our Getting Started to learn more. The complete Javadoc for Rivr is available online.
Support
Support
Quality
Quality
Security
Security
License
License
Reuse
Reuse

kandi-support Support

  • rivr has a low active ecosystem.
  • It has 55 star(s) with 23 fork(s). There are 20 watchers for this library.
  • It had no major release in the last 12 months.
  • There are 9 open issues and 15 have been closed. On average issues are closed in 104 days. There are no pull requests.
  • It has a neutral sentiment in the developer community.
  • The latest version of rivr is v1.0.11
rivr Support
Best in #Build Tool
Average in #Build Tool
rivr Support
Best in #Build Tool
Average in #Build Tool

quality kandi Quality

  • rivr has 0 bugs and 0 code smells.
rivr Quality
Best in #Build Tool
Average in #Build Tool
rivr Quality
Best in #Build Tool
Average in #Build Tool

securitySecurity

  • rivr has no vulnerabilities reported, and its dependent libraries have no vulnerabilities reported.
  • rivr code analysis shows 0 unresolved vulnerabilities.
  • There are 0 security hotspots that need review.
rivr Security
Best in #Build Tool
Average in #Build Tool
rivr Security
Best in #Build Tool
Average in #Build Tool

license License

  • rivr has a Non-SPDX License.
  • Non-SPDX licenses can be open source with a non SPDX compliant license, or non open source licenses, and you need to review them closely before use.
rivr License
Best in #Build Tool
Average in #Build Tool
rivr License
Best in #Build Tool
Average in #Build Tool

buildReuse

  • rivr releases are available to install and integrate.
  • Deployable package is available in Maven.
  • Build file is available. You can build the component from source.
  • It has 11116 lines of code, 1161 functions and 164 files.
  • It has medium code complexity. Code complexity directly impacts maintainability of the code.
rivr Reuse
Best in #Build Tool
Average in #Build Tool
rivr Reuse
Best in #Build Tool
Average in #Build Tool
Top functions reviewed by kandi - BETA

kandi has reviewed rivr and discovered the below as its top functions. This is intended to give you an instant insight into rivr implemented functionality, and help decide if they suit your requirements.

  • Dispatch a message to the given log level .
    • Initialize the servlet .
      • Penders a list of audio items into a prompt element .
        • Appends an item to the buffer .
          • Add a throwable to the array .
            • Process request parameters and files
              • Starts the step .
                • Launches thread .
                  • Converts the global speech recognition information into a new instance .
                    • Parse a duration expression .

                      Get all kandi verified functions for this library.

                      Get all kandi verified functions for this library.

                      rivr Key Features

                      Hello World - a very simple hello world application

                      Voicemail - a prototype voicemail application

                      rivr Examples and Code Snippets

                      See all related Code Snippets

                      R - mgsub problem: substrings being replaced not whole strings

                      copy iconCopydownload iconDownload
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      library(tidyverse)
                      library(data.table)
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly",
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                       "cor", "corner", "corners", "cors", "course", "crse", "court",
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                       "drives", "est", "estate", "estates", "ests", "exp", "expr",
                       "express", "expressway", "expw", "expy", "ext", "extension",
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                       "way", "ways", "well", "wells", "wls"),
                       usps_abbrev = c("aly",
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                            array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                      
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      

                      See all related Code Snippets

                      Community Discussions

                      Trending Discussions on rivr
                      • R - mgsub problem: substrings being replaced not whole strings
                      Trending Discussions on rivr

                      QUESTION

                      R - mgsub problem: substrings being replaced not whole strings

                      Asked 2021-Nov-04 at 19:58

                      I have downloaded the street abbreviations from USPS. Here is the data:

                      dput(usps_streets)
                      structure(list(common_abbrev = c("allee", "alley", "ally", "aly", 
                      "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                      "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                      "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                      "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                      "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                      "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                      "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                      "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                      "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                      "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                      "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                      "cor", "corner", "corners", "cors", "course", "crse", "court", 
                      "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                      "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                      "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                      "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                      "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                      "express", "expressway", "expw", "expy", "ext", "extension", 
                      "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                      "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                      "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                      "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                      "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                      "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                      "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                      "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                      "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                      "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                      "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                      "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                      "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                      "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                      "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                      "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                      "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                      "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                      "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                      "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                      "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                      "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                      "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                      "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                      "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                      "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                      "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                      "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                      "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                      "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                      "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                      "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                      "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                      "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                      "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                      "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                      "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                      "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                      "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                      "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                      "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                      "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                      "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                      "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                      "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                      "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                      "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                      "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                      "way", "ways", "well", "wells", "wls"), usps_abbrev = c("aly", 
                      "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                      "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                      "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                      "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                      "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                      "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                      "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                      "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                      "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                      "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                      "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                      "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                      "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                      "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                      "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                      "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                      "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                      "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                      "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                      "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                      "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                      "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                      "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                      "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                      "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                      "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                      "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                      "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                      "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                      "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                      "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                      "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                      "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                      "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                      "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                      "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                      "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                      "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                      "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                      "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                      "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                      "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                      "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                      "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                      "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                      "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                      "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                      "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                      "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                      "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                      "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                      "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                      "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                      "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                      "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                      "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                      "wall", "way", "way", "ways", "wl", "wls", "wls")), class = "data.frame", row.names = c(NA, 
                      -503L))
                      

                      I would like to use them to work with street addresses and states. Toy data:

                      a <- c("10900 harper ave", "12235 davis annex", "24 van cortland parkway")
                      

                      To convert common abbreviations to the usps abbreviation (standardizing the data), I built a little function:

                      mr_zip <- function(x){
                        x <-textclean::mgsub(usps_streets$common_abbrev, usps_streets$usps_abbrev, x, fixed = T,
                                         order.pattern = T)
                        return(x)
                      }
                      

                      The problem arises when I apply my function to my data:

                      f <- sapply(a, mr_zip)
                      

                      I get the wrong results:

                       "10900 harper avee"       "1235 davis anx" "24 van cortland pkway"
                      

                      Because what I should be getting is:

                      "10900 harper ave"       "1235 davis anx" "24 van cortland pkwy"
                      

                      My questions:

                      1. Why is this happening when I specified order.pattern = T and fixed = T in the mgsub function?
                      2. What can I do to fix it?
                      3. Is there an alternative approach to using vectors in multiple substitution patterns for text?

                      Thanks in advance, all suggestions are welcome.

                      EDIT: Thanks to @RichieSacramento I have found that using the word boundary does help but the function is still incredibly slow when used on a large dataframe (> 400,000 rows). Using safe = TRUE in mgsub leads to the function working properly but it's incredibly slow. Something quick would be desired--hence the bounty.

                      ANSWER

                      Answered 2021-Nov-03 at 10:26
                      Update

                      Here is the benchmarking for the existing to OP's question (borrow test data from @Marek Fiołka but with n <- 10000)

                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      

                      enter image description here

                      where the benchmark code is given as follows

                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      

                      Possible Solutions

                      Perhaps one of the following base R options could help

                      • solution 1
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      • solution 2
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      • solution 3
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      • solution 4 (this is for a special case, i.e., abbreviation for the last word only)
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      

                      output
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      

                      Source https://stackoverflow.com/questions/69467651

                      Community Discussions, Code Snippets contain sources that include Stack Exchange Network

                      Vulnerabilities

                      No vulnerabilities reported

                      Install rivr

                      You can download it from GitHub, Maven.
                      You can use rivr like any standard Java library. Please include the the jar files in your classpath. You can also use any IDE and you can run and debug the rivr component as you would do with any other Java program. Best practice is to use a build tool that supports dependency management such as Maven or Gradle. For Maven installation, please refer maven.apache.org. For Gradle installation, please refer gradle.org .

                      Support

                      We have an email address for limited support: rivr-support@nuecho.com. Also, to report a problem, you can open an issue on GitHub.

                      DOWNLOAD this Library from

                      Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from
                      over 430 million Knowledge Items
                      Find more libraries
                      Reuse Solution Kits and Libraries Curated by Popular Use Cases
                      Explore Kits

                      Save this library and start creating your kit

                      Share this Page

                      share link