kandi background
Explore Kits

crest | Command-line API modeled after JAX-RS | REST library

 by   tomitribe Java Version: Current License: Apache-2.0

 by   tomitribe Java Version: Current License: Apache-2.0

Download this library from

kandi X-RAY | crest Summary

crest is a Java library typically used in Web Services, REST applications. crest has no bugs, it has no vulnerabilities, it has build file available, it has a Permissive License and it has low support. You can download it from GitHub, Maven.
Command-line API styled after JAX-RS. CREST allows you to get to the real work as quickly as possible when writing command line tools in Java. Simply annotate the parameters of any Java method so it can be invoked from a command-line interface with near-zero additional work. Command-registration, help text and validation is taken care of for you.
Support
Support
Quality
Quality
Security
Security
License
License
Reuse
Reuse

kandi-support Support

  • crest has a low active ecosystem.
  • It has 39 star(s) with 17 fork(s). There are 22 watchers for this library.
  • It had no major release in the last 12 months.
  • There are 17 open issues and 11 have been closed. On average issues are closed in 164 days. There are no pull requests.
  • It has a neutral sentiment in the developer community.
  • The latest version of crest is current.
crest Support
Best in #REST
Average in #REST
crest Support
Best in #REST
Average in #REST

quality kandi Quality

  • crest has 0 bugs and 0 code smells.
crest Quality
Best in #REST
Average in #REST
crest Quality
Best in #REST
Average in #REST

securitySecurity

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

license License

  • crest is licensed under the Apache-2.0 License. This license is Permissive.
  • Permissive licenses have the least restrictions, and you can use them in most projects.
crest License
Best in #REST
Average in #REST
crest License
Best in #REST
Average in #REST

buildReuse

  • crest releases are not available. You will need to build from source code and install.
  • Deployable package is available in Maven.
  • Build file is available. You can build the component from source.
  • Installation instructions, examples and code snippets are available.
  • It has 18650 lines of code, 1754 functions and 232 files.
  • It has medium code complexity. Code complexity directly impacts maintainability of the code.
crest Reuse
Best in #REST
Average in #REST
crest Reuse
Best in #REST
Average in #REST
Top functions reviewed by kandi - BETA

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

  • The main entry point .
    • Format a document .
      • Builds the parameters .
        • Fills the bean .
          • Substitute variables in the given string buffer .
            • Wrap a string .
              • Convert the given value to the specified type .
                • Translates string into string array .
                  • Converts a string to arguments .
                    • Load the classes .

                      Get all kandi verified functions for this library.

                      Get all kandi verified functions for this library.

                      crest Key Features

                      Example Executing the Command

                      Help Text

                      @Default values Advanced

                      @Option Lists and Arrays

                      @Default @Option Lists and Arrays

                      @Default and ${variable} Substitution

                      Return Values

                      Stream injections

                      Custom Java Types

                      Custom Validation

                      Maven pom.xml setup

                      Bean Parameter Binding Prefixing options Override defaults Interceptors

                      Maven Archetype

                      Maven Plugin

                      DeltaSpike Annotation Processor

                      Cli module

                      crest Examples and Code Snippets

                      See all related Code Snippets

                      Example

                      copy iconCopydownload iconDownload
                      package org.example.toolz;
                      
                      import org.tomitribe.crest.api.Command;
                      import org.tomitribe.crest.api.Default;
                      import org.tomitribe.crest.api.Option;
                      
                      import java.io.File;
                      import java.net.URI;
                      import java.util.regex.Pattern;
                      
                      public class AnyName {
                      
                          @Command
                          public void rsync(@Option("recursive") boolean recursive,
                                            @Option("links") boolean links,
                                            @Option("perms") boolean perms,
                                            @Option("owner") boolean owner,
                                            @Option("group") boolean group,
                                            @Option("devices") boolean devices,
                                            @Option("specials") boolean specials,
                                            @Option("times") boolean times,
                                            @Option("exclude") Pattern exclude,
                                            @Option("exclude-from") File excludeFrom,
                                            @Option("include") Pattern include,
                                            @Option("include-from") File includeFrom,
                                            @Option("progress") @Default("true") boolean progress,
                                            URI[] sources,
                                            URI dest) {
                      
                              // TODO write the implementation...
                          }
                      }

                      Executing the Command

                      copy iconCopydownload iconDownload
                      $ java -jar target/toolz-1.0.0-SNAPSHOT.jar rsync
                      Missing argument: URI...
                      
                      Usage: rsync [options] URI... URI
                      
                      Options:
                        --devices
                        --exclude=<Pattern>
                        --exclude-from=<File>
                        --group
                        --include=<Pattern>
                        --include-from=<File>
                        --links
                        --owner
                        --perms
                        --no-progress
                        --recursive
                        --specials
                        --times

                      Help Text

                      copy iconCopydownload iconDownload
                      #code
                      # <option> = <description>
                      # <command>.<option> = <description>
                      # The most specific key always wins
                      
                      recursive      = recurse into directories
                      links          = copy symlinks as symlinks
                      perms          = preserve permissions
                      owner          = preserve owner (super-user only)
                      group          = preserve group
                      times          = preserve times
                      devices        = preserve device files (super-user only)
                      specials       = preserve special files
                      exclude        = exclude files matching PATTERN
                      exclude-from   = read exclude patterns from FILE
                      include        = don't exclude files matching PATTERN
                      include-from   = read include patterns from FILE
                      progress       = this is not the description that will be chosen
                      rsync.progress = don't show progress during transfer

                      @Default values

                      copy iconCopydownload iconDownload
                      @Command
                      public void rsync(@Option("exclude") @Default(".*~") Pattern exclude,
                                        @Option("include") Pattern include,
                                        @Option("progress") @Default("true") boolean progress,
                                        URI[] sources,
                                        URI dest) {
                      
                          // TODO write the implementation...
                      }

                      Advanced

                      copy iconCopydownload iconDownload
                      @Command
                      public void myCommand(@Option("myoption") @Default("${env.MY_ENV_VAR}") String exclude) {
                          // TODO write the implementation...
                      }
                      @Command
                      public void myCommand(@Option("myoption") @Default("${sys.MY_ENV_VAR}") String exclude) {
                          // TODO write the implementation...
                      }

                      @Option Lists and Arrays

                      copy iconCopydownload iconDownload
                      @Command
                      public void rsync(@Option("exclude") @Default(".*~") Pattern[] excludes,
                                        @Option("include") Pattern include,
                                        @Option("progress") @Default("true") boolean progress,
                                        URI[] sources,
                                        URI dest) {
                      
                          // TODO write the implementation...
                      }

                      @Default @Option Lists and Arrays

                      copy iconCopydownload iconDownload
                      @Command
                      public void rsync(@Option("exclude") @Default(".*\\.iml,.*\\.iml") Pattern[] excludes,
                                        @Option("include") Pattern include,
                                        @Option("progress") @Default("true") boolean progress,
                                        URI[] sources,
                                        URI dest) {
                      
                      }

                      @Default and ${variable} Substitution

                      copy iconCopydownload iconDownload
                      @Command
                      public void hello(@Option("name") @Default("${user.name}") String user) throws Exception
                          System.out.printf("Hello, %s%n", user);
                      }

                      Return Values

                      copy iconCopydownload iconDownload
                      @Command
                      public String hello(@Option("name") @Default("${user.name}") String user) throws Exception
                          return String.format("Hello, %s%n", user);
                      }

                      Stream injections

                      copy iconCopydownload iconDownload
                      public class IOMe {
                          @org.tomitribe.crest.api.Command
                          public static void asserts(@In final InputStream in,
                                                     @Out final PrintStream out,
                                                     @Err PrintStream err) {
                              // ...
                          }
                      }

                      Custom Validation

                      copy iconCopydownload iconDownload
                      @Command
                      public StreamingOutput cat(final File file) {
                          if (!file.exists()) throw new IllegalStateException("File does not exist: " + file.getAbsolutePath());
                          if (!file.canRead()) throw new IllegalStateException("Not readable: " + file.getAbsolutePath());
                          if (!file.isFile()) throw new IllegalStateException("Not a file: " + file.getAbsolutePath());
                      
                          return new StreamingOutput() {
                              @Override
                              public void write(OutputStream os) throws IOException {
                                  IO.copy(file, os);
                              }
                          };
                      }

                      Maven pom.xml setup

                      copy iconCopydownload iconDownload
                      <?xml version="1.0"?>
                      <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
                          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                        <modelVersion>4.0.0</modelVersion>
                      
                        <groupId>org.example</groupId>
                        <artifactId>toolz</artifactId>
                        <version>0.3-SNAPSHOT</version>
                      
                        <dependencies>
                          <dependency>
                            <groupId>org.tomitribe</groupId>
                            <artifactId>tomitribe-crest</artifactId>
                            <version>0.3-SNAPSHOT</version>
                          </dependency>
                          <dependency>
                            <groupId>junit</groupId>
                            <artifactId>junit</artifactId>
                            <version>4.10</version>
                            <scope>test</scope>
                          </dependency>
                      
                          <!-- Add tomitribe-crest-xbean if you want classpath scanning for @Command -->
                          <dependency>
                            <groupId>org.tomitribe</groupId>
                            <artifactId>tomitribe-crest-xbean</artifactId>
                            <version>0.3-SNAPSHOT</version>
                          </dependency>
                        </dependencies>
                      
                        <build>
                          <defaultGoal>install</defaultGoal>
                          <plugins>
                            <plugin>
                              <artifactId>maven-shade-plugin</artifactId>
                              <version>2.1</version>
                              <executions>
                                <execution>
                                  <phase>package</phase>
                                  <goals>
                                    <goal>shade</goal>
                                  </goals>
                                  <configuration>
                                    <transformers>
                                      <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <mainClass>org.tomitribe.crest.Main</mainClass>
                                      </transformer>
                                    </transformers>
                                  </configuration>
                                </execution>
                              </executions>
                            </plugin>
                          </plugins>
                        </build>
                      
                        <repositories>
                          <repository>
                            <id>sonatype-nexus-snapshots</id>
                            <name>Sonatype Nexus Snapshots</name>
                            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
                            <releases>
                              <enabled>false</enabled>
                            </releases>
                            <snapshots>
                              <enabled>true</enabled>
                            </snapshots>
                          </repository>
                        </repositories>
                      
                      </project>

                      Bean Parameter Binding

                      copy iconCopydownload iconDownload
                      public class ColorfulCmd {
                          @Command
                          public static void exec(final Color color) {
                              // ...
                          }
                      }

                      Prefixing options

                      copy iconCopydownload iconDownload
                      public class ColorfulCmd {
                          @Command
                          public static void exec(@Option("background.") final Color colorBg, @Option("foreground.") final Color colorFg) {
                              // ...
                          }
                      }

                      Override defaults

                      copy iconCopydownload iconDownload
                      public class ColorfulCmd {
                          @Command
                          public static void exec(@Defaults({
                                                      @Defaults.DefaultMapping(name = "r", value = "0"),
                                                      @Defaults.DefaultMapping(name = "g", value = "0"),
                                                      @Defaults.DefaultMapping(name = "b", value = "0"),
                                                      @Defaults.DefaultMapping(name = "a", value = "0")
                                                  })
                                                  @Option("background.")
                                                  final Color colorBg,
                      
                                                  @Defaults({
                                                      @Defaults.DefaultMapping(name = "r", value = "255"),
                                                      @Defaults.DefaultMapping(name = "g", value = "255"),
                                                      @Defaults.DefaultMapping(name = "b", value = "255"),
                                                      @Defaults.DefaultMapping(name = "a", value = "255")
                                                  })
                                                  @Option("foreground.")
                                                  final Color colorFg) {
                              // ...
                          }
                      }

                      Interceptors

                      copy iconCopydownload iconDownload
                      public static class MyInterceptor {
                          @CrestInterceptor
                          public Object intercept(final CrestContext crestContext) {
                              return crestContext.proceed();
                          }
                      }

                      Maven Archetype

                      copy iconCopydownload iconDownload
                      mvn archetype:generate \
                       -DarchetypeGroupId=org.tomitribe \
                       -DarchetypeArtifactId=tomitribe-crest-archetype \
                       -DarchetypeVersion=1.0.0-SNAPSHOT

                      Maven Plugin

                      copy iconCopydownload iconDownload
                      <plugin>
                        <groupId>org.tomitribe</groupId>
                        <version>${crest.version}</version>
                        <artifactId>crest-maven-plugin</artifactId>
                          <executions>
                            <execution>
                              <goals>
                                <goal>descriptor</goal>
                              </goals>
                            </execution>
                          </executions>
                      </plugin>

                      DeltaSpike Annotation Processor

                      copy iconCopydownload iconDownload
                      <dependency>
                        <groupId>org.tomitribe</groupId>
                        <artifactId>tomitribe-crest-generator</artifactId>
                        <version>${crest.version}</version>
                        <scope>provided</scope>
                      </dependency>

                      Cli module

                      copy iconCopydownload iconDownload
                      final CrestCli cli = new CrestCli();
                      cli.run();

                      Create hierarchical summary of XML nodes

                      copy iconCopydownload iconDownload
                      select 
                          replace(trim(regexp_replace(f.path, '[\'\\[\\]@]*' ,''), '$'), '$', ',') as _order
                          ,'<'|| f.value::text || '>' as name
                      from data, table(flatten(input=>xml, recursive=>true)) f
                      where f.key = '@'
                      order by _order;
                      
                      select 
                          REPEAT(' ', regexp_count(_order, '($[0-9]+)*')-1) || name as tree
                      from (
                          select 
                              regexp_replace(f.path, '[\'\\[\\]@]*' ,'') as _order
                              ,'<'|| f.value::text || '>' as name
                          from data, table(flatten(input=>xml, recursive=>true)) f
                          where f.key = '@'
                      )
                      order by _order
                      ;
                      
                      select 
                          replace(trim(regexp_replace(f.path, '[\'\\[\\]@]*' ,''), '$'), '$', ',') as _order
                          ,'<'|| f.value::text || '>' as name
                      from data, table(flatten(input=>xml, recursive=>true)) f
                      where f.key = '@'
                      order by _order;
                      
                      select 
                          REPEAT(' ', regexp_count(_order, '($[0-9]+)*')-1) || name as tree
                      from (
                          select 
                              regexp_replace(f.path, '[\'\\[\\]@]*' ,'') as _order
                              ,'<'|| f.value::text || '>' as name
                          from data, table(flatten(input=>xml, recursive=>true)) f
                          where f.key = '@'
                      )
                      order by _order
                      ;
                      

                      VB.NET DataGridView End Edits and Cell Movment

                      copy iconCopydownload iconDownload
                      Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
                          Dim cbx As New CheckBox
                          cbx.Name = "SelectAll" & ChkName
                          'The box size
                          cbx.Size = New Size(14, 14)
                          cbx.Tag = TagNo
                          cbx.Location = New System.Drawing.Point(XLocation, YLocation)
                          cbx.BackColor = Color.White
                          theDataGridView.Controls.Add(cbx)
                          AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
                          AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                          AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      End Sub
                      
                      AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                      AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      
                      Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
                          If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
                              Return
                          End If
                          Debug.WriteLine("DataGridView_CellContentClick <- Enter")
                          dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
                          Debug.WriteLine("DataGridView_CellContentClick -> Leave")
                      End Sub
                      
                      Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
                          If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
                              Return
                          End If
                           ….. ‘rest of code
                      
                      Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
                          Debug.WriteLine("HeaderCheckBox <- Enter")
                          Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
                          Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
                          dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
                          Dim cbx As CheckBox
                          cbx = DirectCast(sender, CheckBox)
                          Dim rowId As Integer = cbx.Tag
                          RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          For Each row As DataGridViewRow In dgvApp.Rows
                              row.Cells(rowId).Value = cbx.Checked
                          Next
                          AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
                          Debug.WriteLine("HeaderCheckBox -> Leave")
                      End Sub
                      
                      Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
                          Dim cbx As New CheckBox
                          cbx.Name = "SelectAll" & ChkName
                          'The box size
                          cbx.Size = New Size(14, 14)
                          cbx.Tag = TagNo
                          cbx.Location = New System.Drawing.Point(XLocation, YLocation)
                          cbx.BackColor = Color.White
                          theDataGridView.Controls.Add(cbx)
                          AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
                          AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                          AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      End Sub
                      
                      AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                      AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      
                      Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
                          If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
                              Return
                          End If
                          Debug.WriteLine("DataGridView_CellContentClick <- Enter")
                          dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
                          Debug.WriteLine("DataGridView_CellContentClick -> Leave")
                      End Sub
                      
                      Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
                          If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
                              Return
                          End If
                           ….. ‘rest of code
                      
                      Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
                          Debug.WriteLine("HeaderCheckBox <- Enter")
                          Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
                          Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
                          dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
                          Dim cbx As CheckBox
                          cbx = DirectCast(sender, CheckBox)
                          Dim rowId As Integer = cbx.Tag
                          RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          For Each row As DataGridViewRow In dgvApp.Rows
                              row.Cells(rowId).Value = cbx.Checked
                          Next
                          AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
                          Debug.WriteLine("HeaderCheckBox -> Leave")
                      End Sub
                      
                      Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
                          Dim cbx As New CheckBox
                          cbx.Name = "SelectAll" & ChkName
                          'The box size
                          cbx.Size = New Size(14, 14)
                          cbx.Tag = TagNo
                          cbx.Location = New System.Drawing.Point(XLocation, YLocation)
                          cbx.BackColor = Color.White
                          theDataGridView.Controls.Add(cbx)
                          AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
                          AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                          AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      End Sub
                      
                      AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                      AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      
                      Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
                          If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
                              Return
                          End If
                          Debug.WriteLine("DataGridView_CellContentClick <- Enter")
                          dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
                          Debug.WriteLine("DataGridView_CellContentClick -> Leave")
                      End Sub
                      
                      Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
                          If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
                              Return
                          End If
                           ….. ‘rest of code
                      
                      Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
                          Debug.WriteLine("HeaderCheckBox <- Enter")
                          Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
                          Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
                          dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
                          Dim cbx As CheckBox
                          cbx = DirectCast(sender, CheckBox)
                          Dim rowId As Integer = cbx.Tag
                          RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          For Each row As DataGridViewRow In dgvApp.Rows
                              row.Cells(rowId).Value = cbx.Checked
                          Next
                          AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
                          Debug.WriteLine("HeaderCheckBox -> Leave")
                      End Sub
                      
                      Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
                          Dim cbx As New CheckBox
                          cbx.Name = "SelectAll" & ChkName
                          'The box size
                          cbx.Size = New Size(14, 14)
                          cbx.Tag = TagNo
                          cbx.Location = New System.Drawing.Point(XLocation, YLocation)
                          cbx.BackColor = Color.White
                          theDataGridView.Controls.Add(cbx)
                          AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
                          AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                          AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      End Sub
                      
                      AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                      AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      
                      Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
                          If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
                              Return
                          End If
                          Debug.WriteLine("DataGridView_CellContentClick <- Enter")
                          dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
                          Debug.WriteLine("DataGridView_CellContentClick -> Leave")
                      End Sub
                      
                      Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
                          If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
                              Return
                          End If
                           ….. ‘rest of code
                      
                      Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
                          Debug.WriteLine("HeaderCheckBox <- Enter")
                          Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
                          Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
                          dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
                          Dim cbx As CheckBox
                          cbx = DirectCast(sender, CheckBox)
                          Dim rowId As Integer = cbx.Tag
                          RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          For Each row As DataGridViewRow In dgvApp.Rows
                              row.Cells(rowId).Value = cbx.Checked
                          Next
                          AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
                          Debug.WriteLine("HeaderCheckBox -> Leave")
                      End Sub
                      
                      Private Sub AddSAChkBox(ByVal theDataGridView As DataGridView, ByVal XLocation As Integer, ByVal YLocation As Integer, ByVal TagNo As Integer, ByVal ChkName As String)
                          Dim cbx As New CheckBox
                          cbx.Name = "SelectAll" & ChkName
                          'The box size
                          cbx.Size = New Size(14, 14)
                          cbx.Tag = TagNo
                          cbx.Location = New System.Drawing.Point(XLocation, YLocation)
                          cbx.BackColor = Color.White
                          theDataGridView.Controls.Add(cbx)
                          AddHandler cbx.Click, AddressOf HeaderCheckBox_Click
                          AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                          AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      End Sub
                      
                      AddHandler theDataGridView.CellValueChanged, AddressOf DataGridView_CellChecked
                      AddHandler theDataGridView.CurrentCellDirtyStateChanged, AddressOf DataGridView_CurrentCellDirtyStateChanged
                      
                      Private Sub DataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.EventArgs)
                          If (dgvApp.CurrentCell.ColumnIndex < 2 Or dgvApp.CurrentCell.ColumnIndex > 7) Then
                              Return
                          End If
                          Debug.WriteLine("DataGridView_CellContentClick <- Enter")
                          dgvApp.CommitEdit(DataGridViewDataErrorContexts.Commit)
                          Debug.WriteLine("DataGridView_CellContentClick -> Leave")
                      End Sub
                      
                      Private Sub DataGridView_CellChecked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs)
                          If (e.ColumnIndex < 2 Or e.ColumnIndex > 7) Then
                              Return
                          End If
                           ….. ‘rest of code
                      
                      Private Sub HeaderCheckBox_Click(ByVal sender As Object, ByVal e As EventArgs)
                          Debug.WriteLine("HeaderCheckBox <- Enter")
                          Dim curCellRowIndex = dgvApp.CurrentCell.RowIndex
                          Dim curCellColIndex = dgvApp.CurrentCell.ColumnIndex
                          dgvApp.CurrentCell = dgvApp.Rows(0).Cells(0)
                          Dim cbx As CheckBox
                          cbx = DirectCast(sender, CheckBox)
                          Dim rowId As Integer = cbx.Tag
                          RemoveHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          For Each row As DataGridViewRow In dgvApp.Rows
                              row.Cells(rowId).Value = cbx.Checked
                          Next
                          AddHandler dgvApp.CellValueChanged, AddressOf DataGridView_CellValueChanged
                          dgvApp.CurrentCell = dgvApp.Rows(curCellRowIndex).Cells(curCellColIndex)
                          Debug.WriteLine("HeaderCheckBox -> Leave")
                      End Sub
                      

                      Creating a new dataframe with averages from another dataframe with multiple conditions in R

                      copy iconCopydownload iconDownload
                      library(dplyr)
                      df %>% 
                          group_by(SITE, ZONE) %>% 
                          summarise(
                              across(where(is.numeric), mean)
                          )
                      # A tibble: 15 x 8
                      # Groups:   SITE [3]
                         SITE  ZONE       C_TOTAL    C_M2 TRANS_A SCARID_T ACAN_T  SIG_T
                         <fct> <fct>        <dbl>   <dbl>   <dbl>    <dbl>  <dbl>  <dbl>
                       1 1     CREST        20    0.0996     213.   12      4.33   3.67 
                       2 1     INNER_FLAT    3    0.0111     265.    0.333  0.333  2.33 
                       3 1     MID_FLAT      2.33 0.00684    339.    0      0      2.33 
                       4 1     OUTER_FLAT   52    0.283      179.   38.3    7.67   6    
                       5 1     SLOPE        23.7  0.110      222.   14.3    5      4.33 
                       6 2     CREST        25.3  0.148      178.   14      0     11.3  
                       7 2     INNER_FLAT    2.67 0.00973    297.    2      0.667  0    
                       8 2     MID_FLAT     11    0.0342     296.   10.7    0.333  0    
                       9 2     OUTER_FLAT   69    0.402      166.   66      0.667  2.33 
                      10 2     SLOPE         6    0.0296     206.    5.67   0      0.333
                      11 3     CREST        18.3  0.0928     179.   10      1.67   6.67 
                      12 3     INNER_FLAT   18.3  0.0748     256.   15.3    0      3    
                      13 3     MID_FLAT      5.33 0.0149     355.    3.33   1.67   0.333
                      14 3     OUTER_FLAT   42.3  0.241      191.   38.3    0.333  3.67 
                      15 3     SLOPE         2    0.0106     193.    1      0      1
                      

                      Curve fitting in Python, need almost exact match of the shape of the curve rather than a curve that minimize mean square difference

                      copy iconCopydownload iconDownload
                      import matplotlib.pyplot as plt
                      import numpy as np
                      from scipy.integrate import cumtrapz
                      from scipy.optimize import curve_fit
                      
                      def signal( x, A, B, C, D, E, F ):
                          ### note: C, D, E, F have different meaning here
                          r = (
                              A * x**2 
                              + B * x
                              + C
                              + D * np.sin( F * x )
                              + E * np.cos( F * x )
                          )
                          return r
                      
                      def signal_p( x, A, B, C, D, E, F ):
                          r = (
                              A * x**2 
                              + B * x
                              + C * np.sin( D * x - E )
                              + F
                          )
                          return r
                      
                      testparams = [ -1, 1, 3, 0.005, 0.03, 22 ]
                      
                      ### test data with noise
                      xl = np.linspace( -0.3, 1.6, 190 )
                      sl = signal( xl, *testparams )
                      sl += np.random.normal( size=len( xl ), scale=0.005 )
                      
                      ### numerical integrals
                      Sl = cumtrapz( sl, x=xl, initial=0 )
                      SSl = cumtrapz( Sl, x=xl, initial=0 )
                      
                      ### fitting the integro-differential equation to get the frequency
                      """
                      note: 
                          with y = A x**2 +...+ D sin() + E cos()
                          the double integral int( int(y) ) = a x**4 + ... - y/F**2
                      """
                      VMXT = np.array( [ xl**4, xl**3, xl**2, xl, np.ones( len( xl ) ), sl ] )
                      VMX = VMXT.transpose()
                      
                      A = np.dot( VMXT, VMX )
                      SV = np.dot( VMXT, SSl )
                      AI = np.linalg.inv( A )
                      result = np.dot( AI , SV )
                      print ( "Fit: ",result )
                      F = np.sqrt( -1 / result[-1] )
                      print("F = ", F)
                      
                      ### Fitting the linear parameters with the frequency known
                      VMXT = np.array( 
                          [
                              xl**2, xl, np.ones( len( xl ) ),
                              np.sin( F * xl), np.cos( F * xl )
                          ]
                      )
                      VMX = VMXT.transpose()
                      
                      A = np.dot( VMXT, VMX )
                      SV = np.dot( VMXT, sl )
                      AI = np.linalg.inv( A )
                      A, B, C, D, E = np.dot( AI , SV )
                      print( A, B, C, D, E )
                      
                      ### Non-linear fit with initial guesses
                      amp = np.sqrt( D**2 + E**2 )
                      phi = -np.arctan( D / E )
                      opt, cov = curve_fit( signal_p, xl, sl, p0=( A, B, amp, F, phi, C ) )
                      print( opt )
                      
                      ### plotting
                      fig = plt.figure()
                      ax = fig.add_subplot( 1, 1, 1 )
                      
                      ax.plot(
                          xl, sl,
                          ls='', marker='+', label="data", markersize=5
                      )
                      ax.plot(
                          xl, signal( xl, A, B, C, D, E, F ),
                          ls="--", label="double linear fit"
                      )
                      ax.plot(
                          xl, signal_p( xl, *opt ),
                          ls=":", label="non-linear"
                      )
                      ax.legend( loc=0 )
                      ax.grid()
                      plt.show()
                      
                      Fit:  [-0.083161  0.1659759 1.49879056 0.848999 0.130222 -0.001990]
                      F =  22.414133356157887
                       -0.998516    0.998429    3.000265    0.012701    0.026926
                      [-0.99856269  0.9973273   0.0305014  21.96402992 -1.4215656   3.00100979]
                      
                      import matplotlib.pyplot as plt
                      import numpy as np
                      from scipy.integrate import cumtrapz
                      from scipy.optimize import curve_fit
                      
                      def signal( x, A, B, C, D, E, F ):
                          ### note: C, D, E, F have different meaning here
                          r = (
                              A * x**2 
                              + B * x
                              + C
                              + D * np.sin( F * x )
                              + E * np.cos( F * x )
                          )
                          return r
                      
                      def signal_p( x, A, B, C, D, E, F ):
                          r = (
                              A * x**2 
                              + B * x
                              + C * np.sin( D * x - E )
                              + F
                          )
                          return r
                      
                      testparams = [ -1, 1, 3, 0.005, 0.03, 22 ]
                      
                      ### test data with noise
                      xl = np.linspace( -0.3, 1.6, 190 )
                      sl = signal( xl, *testparams )
                      sl += np.random.normal( size=len( xl ), scale=0.005 )
                      
                      ### numerical integrals
                      Sl = cumtrapz( sl, x=xl, initial=0 )
                      SSl = cumtrapz( Sl, x=xl, initial=0 )
                      
                      ### fitting the integro-differential equation to get the frequency
                      """
                      note: 
                          with y = A x**2 +...+ D sin() + E cos()
                          the double integral int( int(y) ) = a x**4 + ... - y/F**2
                      """
                      VMXT = np.array( [ xl**4, xl**3, xl**2, xl, np.ones( len( xl ) ), sl ] )
                      VMX = VMXT.transpose()
                      
                      A = np.dot( VMXT, VMX )
                      SV = np.dot( VMXT, SSl )
                      AI = np.linalg.inv( A )
                      result = np.dot( AI , SV )
                      print ( "Fit: ",result )
                      F = np.sqrt( -1 / result[-1] )
                      print("F = ", F)
                      
                      ### Fitting the linear parameters with the frequency known
                      VMXT = np.array( 
                          [
                              xl**2, xl, np.ones( len( xl ) ),
                              np.sin( F * xl), np.cos( F * xl )
                          ]
                      )
                      VMX = VMXT.transpose()
                      
                      A = np.dot( VMXT, VMX )
                      SV = np.dot( VMXT, sl )
                      AI = np.linalg.inv( A )
                      A, B, C, D, E = np.dot( AI , SV )
                      print( A, B, C, D, E )
                      
                      ### Non-linear fit with initial guesses
                      amp = np.sqrt( D**2 + E**2 )
                      phi = -np.arctan( D / E )
                      opt, cov = curve_fit( signal_p, xl, sl, p0=( A, B, amp, F, phi, C ) )
                      print( opt )
                      
                      ### plotting
                      fig = plt.figure()
                      ax = fig.add_subplot( 1, 1, 1 )
                      
                      ax.plot(
                          xl, sl,
                          ls='', marker='+', label="data", markersize=5
                      )
                      ax.plot(
                          xl, signal( xl, A, B, C, D, E, F ),
                          ls="--", label="double linear fit"
                      )
                      ax.plot(
                          xl, signal_p( xl, *opt ),
                          ls=":", label="non-linear"
                      )
                      ax.legend( loc=0 )
                      ax.grid()
                      plt.show()
                      
                      Fit:  [-0.083161  0.1659759 1.49879056 0.848999 0.130222 -0.001990]
                      F =  22.414133356157887
                       -0.998516    0.998429    3.000265    0.012701    0.026926
                      [-0.99856269  0.9973273   0.0305014  21.96402992 -1.4215656   3.00100979]
                      

                      Two ranges/ color-scales within one seaborn heatmap

                      copy iconCopydownload iconDownload
                      import numpy as np
                      import matplotlib.pyplot as plt
                      import seaborn as sns
                      
                      values = np.random.uniform(0, 7000, size=(50, 50))
                      vmax = np.amax(values)
                      
                      fig, (ax1, ax2) = plt.subplots(ncols=2)
                      
                      sns.heatmap(values, ax=ax1, vmin=25, vmax=vmax, cmap="crest")
                      sns.heatmap(values, ax=ax2, vmin=0, vmax=25, cmap="flare")
                      
                      for ax in (ax1, ax2):
                          ax.set_xlabel("Time")
                          ax.set_ylabel("Method")
                      
                      plt.show()
                      
                      import matplotlib.pyplot as plt
                      import seaborn as sns
                      import numpy as np
                      from scipy.ndimage.filters import gaussian_filter
                      
                      # first create some test data
                      data = gaussian_filter(np.random.rand(50, 50), sigma=5)
                      data -= data.min()
                      data *= 2 / data.max()
                      data = np.where(data < 1, data*25, (data ** 3) * 25)
                      
                      # adapt the colormaps such that the "under" or "over" color is "none"
                      cmap1 = plt.get_cmap('crest').copy()
                      cmap1.set_under('none')
                      cmap2 = plt.get_cmap('flare').copy()
                      cmap2.set_over('none')
                      
                      ax1 = sns.heatmap(data, vmin=25, cmap=cmap1, cbar_kws={'pad': -0.02})
                      sns.heatmap(data, vmin=0, vmax=25, cmap=cmap2, ax=ax1)
                      
                      plt.show()
                      
                      import seaborn as sns
                      import matplotlib as mpl
                      import matplotlib.pyplot as plt
                      
                      class SplitCMap(mpl.colors.Colormap):
                        def __init__(self, name, vmin, vsplit, vmax, N=256):
                          super().__init__(name, N)
                          self.lowcmap = mpl.cm.get_cmap('flare')
                          self.highcmap = mpl.cm.get_cmap('crest')
                          self.split_level = (vsplit-vmin) / (vmax-vmin)
                          self.scale_low = 1.0 / self.split_level
                          self.scale_high = 1.0 / (1.0 - self.split_level)
                        def mapcolor(self, v, **kwds):
                          if v < self.split_level:
                            return self.lowcmap(v * self.scale_low, **kwds)
                          return self.highcmap((v-self.split_level)*self.scale_high, **kwds)
                        def __call__(self, *args, **kwds):
                          if isinstance(args[0], (int, float)):
                            self.mapcolor(args[0], **kwds)
                          return [self.mapcolor(v, **kwds) for v in args[0] ]
                      
                      df = pd.DataFrame(merged, classes)
                      
                      vmax = np.amax(merged)
                      
                      cmap = SplitCMap('split', 0, 25, vmax)
                      ax = sns.heatmap(df, cmap=cmap)
                      
                      plt.xlabel("Time")
                      plt.ylabel("Method")
                      
                      plt.show()
                      

                      Is there a way to return float or integer from a conditional True/False

                      copy iconCopydownload iconDownload
                      def marker_up(row):
                          n_row = [row[col] for col in columns if "Buu" in col]
                          n_high = row['Crest']
                          if n_high in n_row:
                              q = bs.bisect_left(n_row, n_high)
                              q = max(0, q)
                              return q
                          else:
                              q_mod = bs.bisect_left(n_row, n_high)
                              q_mod -= 1
                              q_mod = max(0, q_mod)
                              return q_mod
                      
                      Marker_up = df.apply(marker_up, axis=1)
                      
                      def marker_down(row):
                          n_row = [row[col] for col in columns if "See" in col]
                          n_low = row['Trough']
                          if n_low in n_row:
                              q = bs.bisect_left(n_row, n_low)
                              q = q - len(n_row)
                              return q
                          else:
                              q_mod = bs.bisect_left(n_row, n_low)
                              q_mod = q_mod - len(n_row)
                              return q_mod
                      
                      Marker_down = df.apply(marker_down, axis=1)
                      df['Marker'] = Marker_up + Marker_down
                      
                      return df
                      
                      df['Marker'] = np.searchsorted(df['Crest'], list)
                      

                      Keyframes not animating height

                      copy iconCopydownload iconDownload
                      .swipe {
                        animation-name: swipeWidthHeight, close;
                        animation-duration: 0.3s, 2s; /* missing comma */
                        animation-delay: 1s, 10s;
                        animation-fill-mode: forwards, forwards;
                        display: flex;
                        justify-content: center;
                        height: 179px;
                        width: 434px;
                        background-color: #fff;
                      }
                      

                      VBA partial match between two column

                      copy iconCopydownload iconDownload
                      Option Explicit
                      
                      Sub ROITown()
                      
                          Dim s As String, arTown, LastRow As Long
                          Dim r As Long, i As Long, n As Long
                          
                          With Worksheets("LookUp") ' towns
                               LastRow = .Cells(.Rows.Count, "E").End(xlUp).Row
                               arTown = .Range("E2:E" & LastRow)
                          End With
                          
                          With Worksheets("RO") ' address
                              LastRow = .Cells(.Rows.Count, "K").End(xlUp).Row
                              For r = 2 To LastRow
                                  For i = 1 To UBound(arTown)
                                      s = Trim(arTown(i, 1))
                                      If InStr(1, .Cells(r, "K"), s, vbTextCompare) Then
                                          .Cells(r, "Q") = s
                                          n = n + 1
                                          Exit For
                                      End If
                                  Next
                              Next r
                          End With
                          MsgBox n & " rows updated", vbInformation
                          
                      End Sub
                      
                      =IFERROR(INDEX(Lookup,AGGREGATE(15,6,1/(ISNUMBER(SEARCH(Lookup[Lookup],TRANSPOSE(LongName[Towns]))))*ROW(Lookup)-ROW(Lookup[#Headers]),ROW(INDEX($A:$A,1):INDEX($A:$A,ROWS(Lookup))))),"")
                      

                      A function that takes an array of strings and return a filtered array containing the same element but with the other array elements removed

                      copy iconCopydownload iconDownload
                      geese = ["African", "Roman Tufted", "Toulouse", "Pilgrim", "Steinbacher"]
                      birds = ["Mallard", "Hook Bill", "African", "Crested", "Pilgrim", "Toulouse", "Blue Swedish"]
                      def goose_filter(birds,geese):
                         for i in birds:
                            if i in geese:
                                birds.remove(i)
                         return birds
                      print(goose_filter(birds,geese)) #['Mallard', 'Hook Bill', 'Crested', 'Toulouse', 'Blue Swedish']
                      

                      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",