kandi background
Explore Kits

btm | JTA Transaction Manager | REST library

 by   bitronix Java Version: Current License: Apache-2.0

 by   bitronix Java Version: Current License: Apache-2.0

Download this library from

kandi X-RAY | btm Summary

btm is a Java library typically used in Web Services, REST, Nodejs applications. btm 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.
The Bitronix Transaction Manager (BTM) is a simple but complete implementation of the JTA 1.1 API. It is a fully working XA transaction manager that provides all services required by the JTA API while trying to keep the code as simple as possible for easier understanding of the XA semantics.
Support
Support
Quality
Quality
Security
Security
License
License
Reuse
Reuse

kandi-support Support

  • btm has a low active ecosystem.
  • It has 393 star(s) with 131 fork(s). There are 46 watchers for this library.
  • It had no major release in the last 12 months.
  • There are 18 open issues and 45 have been closed. On average issues are closed in 149 days. There are 8 open pull requests and 0 closed requests.
  • It has a neutral sentiment in the developer community.
  • The latest version of btm is current.
btm Support
Best in #REST
Average in #REST
btm Support
Best in #REST
Average in #REST

quality kandi Quality

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

securitySecurity

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

license License

  • btm 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.
btm License
Best in #REST
Average in #REST
btm License
Best in #REST
Average in #REST

buildReuse

  • btm releases are not available. You will need to build from source code and install.
  • Build file is available. You can build the component from source.
  • btm saves you 11209 person hours of effort in developing the same functionality from scratch.
  • It has 22701 lines of code, 1963 functions and 256 files.
  • It has medium code complexity. Code complexity directly impacts maintainability of the code.
btm Reuse
Best in #REST
Average in #REST
btm Reuse
Best in #REST
Average in #REST
Top functions reviewed by kandi - BETA

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

  • Reads log records into a collection of transactions .
    • Returns a connection handle from the pool
      • Perform the recovery process .
        • Counts the number of transactions by the given status .
          • Called when a session is being closed .
            • Sets the target property on the specified target object .
              • Enlist this XAResource with the given XAResource state .
                • Build the server id array .
                  • Starts the jobs for the given resource .
                    • Delist a XAResource .

                      Get all kandi verified functions for this library.

                      Get all kandi verified functions for this library.

                      btm Key Features

                      Greater use of low-contention lock collections throughout the code and the removal of most large-grained locks.

                      A new connection pool with a focus on zero-wait connection acquisition and concurrent expansion/contraction. For one large workload compared to BTM 2.1 total connection wait time went from 76 seconds to 372ms, and lock contentions went from 26269 to just 21.

                      High-performance proxies around javax.sql entities via bytecode generation with support for Javassist, and cglib, with fallback to java.lang.reflect.proxy. Javassist and cglib offer substantial performance increases and are strongly recommended.

                      High-performance transaction log journaling using a new design that allows concurrent appenders through a write-reservation model. The new journal is 3-14x faster than BTM 2.1.

                      Support for all levels of JDBC upto and including JDBC 4.1

                      OSGi support

                      A change of license from LGPL v3 to Apache 2.

                      Overview

                      FAQ

                      Transaction manager configuration

                      Resource loader configuration

                      Docker-Compose with Commandbox cannot change web root

                      copy iconCopydownload iconDownload
                      image: ortussolutions/commandbox:lucee5
                      
                      ENV APP_DIR=/app/public
                      
                      image: ortussolutions/commandbox:lucee5
                      
                      ENV APP_DIR=/app/public
                      

                      User Specific Page in TODO List

                      copy iconCopydownload iconDownload
                      <div class = "divname">
                      
                        {% if user.is_authenticated %}
                            Hello,{{user.username}}<br>
                            <a href="accounts/logout">Logout</a>
                        {% else%}
                            <a href="accounts/register">Register</a>
                            <a href="accounts/login1">login</a>
                        {% endif %}
                      
                      </div>
                      
                      <div class="p-3 mb-2 bg-warning text-dark" style="text-align: center;">
                      
                      <form method="POST" >
                          {% csrf_token %} 
                          {{form}}
                           <input class='btn btm-sm btn-info' type='submit' name='add' value="add" > 
                      </form>
                      
                      
                      {% for val in values %}
                          
                          {% if val.complete == True %}
                          <s>{{val}}</s>
                          {% else %}
                          <span>{{val}}</span>
                          {% endif %}
                          
                          <form action="{% url 'deldata' val.id %}" method="POST" class="in-line">
                              {% csrf_token %}
                              <input class='btn btm-sm btn-danger' type="submit" name="delete" value="delete">
                              <a  class='btn btm-sm btn-info' href="{% url 'update' val.id %}" >Update</a>
                          </form>
                          
                       {% endfor %}
                      </div>
                      
                      from django import forms
                      from django.forms import ModelForm
                      from .models import value
                      from django import forms
                      
                      class TitleForm(forms.ModelForm):
                          class Meta:
                             model= value
                             fields=['task', 'complete', 'created']
                      
                      def home(request):
                          values=value.objects.all()    
                          form=TitleForm()
                          if request.method=='POST':
                               form=TitleForm(request.POST)
                               if form.is_valid():
                                   new_data=form.save(commit=False)
                                   new_data.owner=request.user
                                   new_data.save()
                               return HttpResponseRedirect('/')
                          context={'form':form,'values':values}
                          return render(request,'home.html',context)
                      
                      <div class = "divname">
                      
                        {% if user.is_authenticated %}
                            Hello,{{user.username}}<br>
                            <a href="accounts/logout">Logout</a>
                        {% else%}
                            <a href="accounts/register">Register</a>
                            <a href="accounts/login1">login</a>
                        {% endif %}
                      
                      </div>
                      
                      <div class="p-3 mb-2 bg-warning text-dark" style="text-align: center;">
                      
                      <form method="POST" >
                          {% csrf_token %} 
                          {{form}}
                           <input class='btn btm-sm btn-info' type='submit' name='add' value="add" > 
                      </form>
                      
                      
                      {% for val in values %}
                          
                          {% if val.complete == True %}
                          <s>{{val}}</s>
                          {% else %}
                          <span>{{val}}</span>
                          {% endif %}
                          
                          <form action="{% url 'deldata' val.id %}" method="POST" class="in-line">
                              {% csrf_token %}
                              <input class='btn btm-sm btn-danger' type="submit" name="delete" value="delete">
                              <a  class='btn btm-sm btn-info' href="{% url 'update' val.id %}" >Update</a>
                          </form>
                          
                       {% endfor %}
                      </div>
                      
                      from django import forms
                      from django.forms import ModelForm
                      from .models import value
                      from django import forms
                      
                      class TitleForm(forms.ModelForm):
                          class Meta:
                             model= value
                             fields=['task', 'complete', 'created']
                      
                      def home(request):
                          values=value.objects.all()    
                          form=TitleForm()
                          if request.method=='POST':
                               form=TitleForm(request.POST)
                               if form.is_valid():
                                   new_data=form.save(commit=False)
                                   new_data.owner=request.user
                                   new_data.save()
                               return HttpResponseRedirect('/')
                          context={'form':form,'values':values}
                          return render(request,'home.html',context)
                      
                      <div class = "divname">
                      
                        {% if user.is_authenticated %}
                            Hello,{{user.username}}<br>
                            <a href="accounts/logout">Logout</a>
                        {% else%}
                            <a href="accounts/register">Register</a>
                            <a href="accounts/login1">login</a>
                        {% endif %}
                      
                      </div>
                      
                      <div class="p-3 mb-2 bg-warning text-dark" style="text-align: center;">
                      
                      <form method="POST" >
                          {% csrf_token %} 
                          {{form}}
                           <input class='btn btm-sm btn-info' type='submit' name='add' value="add" > 
                      </form>
                      
                      
                      {% for val in values %}
                          
                          {% if val.complete == True %}
                          <s>{{val}}</s>
                          {% else %}
                          <span>{{val}}</span>
                          {% endif %}
                          
                          <form action="{% url 'deldata' val.id %}" method="POST" class="in-line">
                              {% csrf_token %}
                              <input class='btn btm-sm btn-danger' type="submit" name="delete" value="delete">
                              <a  class='btn btm-sm btn-info' href="{% url 'update' val.id %}" >Update</a>
                          </form>
                          
                       {% endfor %}
                      </div>
                      
                      from django import forms
                      from django.forms import ModelForm
                      from .models import value
                      from django import forms
                      
                      class TitleForm(forms.ModelForm):
                          class Meta:
                             model= value
                             fields=['task', 'complete', 'created']
                      
                      def home(request):
                          values=value.objects.all()    
                          form=TitleForm()
                          if request.method=='POST':
                               form=TitleForm(request.POST)
                               if form.is_valid():
                                   new_data=form.save(commit=False)
                                   new_data.owner=request.user
                                   new_data.save()
                               return HttpResponseRedirect('/')
                          context={'form':form,'values':values}
                          return render(request,'home.html',context)
                      

                      Why are two select statements (on array of custom objects) are combined into one result?

                      copy iconCopydownload iconDownload
                      cls
                      $customObjectsArray = @() 
                      
                      #### build a few objects for to reproduce my issue #### 
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo1.odx" 
                      Type     = "Orchestration"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo1.odx"
                      Type     = "Orchestration"
                      Method   = "GetUser"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo9.btm"
                      Type     = "Map"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo2.btm"
                      Type     = "Map"
                      Method   = "GetConnString"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo4.odx"
                      Type     = "Orchestration"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      "`n`n============================`n"
                      
                      'Grouped By MethodName... Count of Items = $($customObjectsArray.Count)' 
                      $Tally = $customObjectsArray | Group Method 
                      $Tally | Sort Count -Descending | FT Name, Count 
                      
                      "`n`n============================`n"
                      
                      'Grouped By MethodName, FileType... Count of Items = $($customObjectsArray.Count)' 
                      $Tally2 = $customObjectsArray | Group Method, Type
                      $Tally2 |  Sort Count -Descending | Ft Name, Count 
                      
                      
                      ============================
                      
                      Grouped By MethodName... Count of Items = $($customObjectsArray.Count)
                      
                      Name          Count
                      ----          -----
                      GetText           3
                      GetUser           1
                      GetConnString     1
                      
                      
                      
                      
                      ============================
                      
                      Grouped By MethodName, FileType... Count of Items = $($customObjectsArray.Count)
                      
                      Name                   Count
                      ----                   -----
                      GetText, Orchestration     2
                      GetUser, Orchestration     1
                      GetText, Map               1
                      GetConnString, Map         1
                      
                      
                      PS> 
                      
                      cls
                      $customObjectsArray = @() 
                      
                      #### build a few objects for to reproduce my issue #### 
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo1.odx" 
                      Type     = "Orchestration"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo1.odx"
                      Type     = "Orchestration"
                      Method   = "GetUser"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo9.btm"
                      Type     = "Map"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo2.btm"
                      Type     = "Map"
                      Method   = "GetConnString"
                      }
                      $customObjectsArray += $customObject
                      
                      $customObject = New-Object -TypeName psobject -Property @{
                      FileName = "Demo4.odx"
                      Type     = "Orchestration"
                      Method   = "GetText"
                      }
                      $customObjectsArray += $customObject
                      
                      "`n`n============================`n"
                      
                      'Grouped By MethodName... Count of Items = $($customObjectsArray.Count)' 
                      $Tally = $customObjectsArray | Group Method 
                      $Tally | Sort Count -Descending | FT Name, Count 
                      
                      "`n`n============================`n"
                      
                      'Grouped By MethodName, FileType... Count of Items = $($customObjectsArray.Count)' 
                      $Tally2 = $customObjectsArray | Group Method, Type
                      $Tally2 |  Sort Count -Descending | Ft Name, Count 
                      
                      
                      ============================
                      
                      Grouped By MethodName... Count of Items = $($customObjectsArray.Count)
                      
                      Name          Count
                      ----          -----
                      GetText           3
                      GetUser           1
                      GetConnString     1
                      
                      
                      
                      
                      ============================
                      
                      Grouped By MethodName, FileType... Count of Items = $($customObjectsArray.Count)
                      
                      Name                   Count
                      ----                   -----
                      GetText, Orchestration     2
                      GetUser, Orchestration     1
                      GetText, Map               1
                      GetConnString, Map         1
                      
                      
                      PS> 
                      

                      BradleyTerry2 package in R - Using null hypothesis as reference player

                      copy iconCopydownload iconDownload
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      
                      anova(BTv1, test = "Chisq")
                      
                      Sequential Wald Tests
                      
                      Model: binomial, link: logit
                      
                      Response: NULL
                      
                      Predictor: ~Characters + (1 | ID)
                      
                      Terms added sequentially (first to last)
                      
                                 Statistic Df P(>|Chi|)   
                      NULL                                
                      Characters    46.116 26  0.008853 **
                      ---
                      Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
                      
                      cf <- coef(BTv2)[!is.na(coef(BTv2))]
                      V <- vcov(BTv2)
                      ind <- grep("Matchup", names(cf))
                      chisq <- c(t(cf[ind]) %*% chol2inv(chol(V[ind, ind])) %*% cf[ind])
                      df <- length(ind)
                      c(chisq = chisq, df = df)  
                      #     chisq       df 
                      #  107.5667 167.0000 
                      
                      summary(BTv2)$fixef
                      #                              Estimate Std. Error    z value  Pr(>|z|)
                      # MatchupCaptainFalcon;Falco -0.1327177  0.3161729 -0.4197632 0.6746585
                      # MatchupCaptainFalcon;Peach  0.1464518  0.3861823  0.3792297 0.7045173
                      # MatchupFalco;Peach         -0.4103029  0.3365761 -1.2190496 0.2228254
                      
                      summary(BTv1)$fixef[c("CharactersFalco", "CharactersPeach"),]
                      #                 Estimate Std. Error  z value   Pr(>|z|)
                      # CharactersFalco 2.038925  0.9576332 2.129130 0.03324354
                      # CharactersPeach 2.119304  0.9508804 2.228781 0.02582845
                      
                      # add in character with fixed effect set to zero (Bowser)
                      V <- cbind(XCharactersBowser = 0, rbind(XCharactersBowser = 0, 
                      vcov(BTv1)))
                      cf <- c(CharactersBowser = 0, coef(BTv1))
                      # compute quasi-variances
                      qv <- qvcalc(V, "XCharacters", estimates = cf,
                                   labels = sub("Characters", "", names(cf)))
                      # plot and compare 
                      # (need to set ylim because some estimates are essentially infinite)
                      par(mar = c(7, 4, 3, 1)) 
                      plot(qv, ylim = c(-5, 5))
                      

                      Matplotlib - Several lines on the same plot

                      copy iconCopydownload iconDownload
                      def flist_from_slist(data):
                         return [float(x.replace(' ', '')) for x in data]
                      

                      R - mgsub problem: substrings being replaced not whole strings

                      copy iconCopydownload iconDownload
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      > mb1
                      Unit: milliseconds
                                                    expr       min        lq       mean    median
                                f_MK_conv2(df$addresses) 1409.0643 1470.3992 1612.09037 1631.3014
                       f_MK_replaceString(df, addresses)   50.1582   54.3035   94.53149   62.5772
                                    f_TIC1(df$addresses)  394.5972  420.3283  461.50675  447.6186
                                    f_TIC2(df$addresses) 1579.1868 1852.6873 2052.28388 1964.8845
                                    f_TIC3(df$addresses)   65.8436   71.5448   93.36210   84.9698
                              uq       max neval
                       1710.3459 1898.6773    20
                        116.3108  264.2616    20
                        499.4052  626.9240    20
                       2246.5562 2916.2253    20
                        102.7689  183.5121    20
                      
                      f_MK_conv2 <- function(x) {
                        USPSv <- array(
                          data = USPS$usps_abbrev,
                          dimnames = list(USPS$common_abbrev)
                        )
                        USPS_conv2 <- function(x) {
                          t <- str_split(x, " ")
                          comm <- t[[1]][length(t[[1]])]
                          str_replace(x, comm, USPSv[comm])
                        }
                        Vectorize(USPS_conv2)(x)
                      }
                      
                      f_MK_replaceString <- function(.data, value) {
                        ht.create <- function() new.env()
                      
                        ht.insert <- function(ht, key, value) ht[[key]] <- value
                        ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                        ht.lookup <- function(ht, key) ht[[key]]
                        ht.lookup <- Vectorize(ht.lookup, "key")
                      
                        ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                        ht.delete <- Vectorize(ht.delete, "key")
                      
                        addHashTable2 <- function(.x, .y, key, value) {
                          key <- enquo(key)
                          value <- enquo(value)
                      
                          if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                            stop(paste0(
                              "`.y` must contain `", as_label(key),
                              "` and `", as_label(value), "` columns"
                            ))
                          }
                      
                          if ((.y %>% distinct(!!key, !!value) %>% nrow()) !=
                            (.y %>% distinct(!!key) %>% nrow())) {
                            warning(paste0(
                              "\nThe number of unique values of the ", as_label(key),
                              " variable is different\n",
                              " from the number of unique values of the ",
                              as_label(key), " and ", as_label(value), " pairs!\n",
                              "The dictionary will only return the last values for a given key!"
                            ))
                          }
                      
                          ht <- ht.create()
                          ht %>% ht.insert(
                            .y %>% distinct(!!key, !!value) %>% pull(!!key),
                            .y %>% distinct(!!key, !!value) %>% pull(!!value)
                          )
                          attr(.x, "hashTab") <- ht
                          .x
                        }
                      
                        .data <- .data %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                        value <- enquo(value)
                        # Test whether the value variable is in .data
                        if (!(as_label(value) %in% names(.data))) {
                          stop(paste(
                            "The", as_label(value),
                            "variable does not exist in the .data table!"
                          ))
                        }
                      
                        # Dictionary attribute presence test
                        if (!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."
                          ))
                        }
                      
                        txt <- .data %>% pull(!!value)
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(
                            attr(.data, "hashTab"),
                            str_sub(txt, start = i + 1)
                          )
                        )
                        .data %>% mutate(!!value := txt)
                      }
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      f_JM <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                      
                        x$usps_abbreviation <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                      }
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_MK_replaceString(df, addresses),
                        f_JM(df),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        f_TIC3(df$addresses),
                        f_TIC4(df$addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      f_TIC1 <- function(x) {
                        sapply(
                          strsplit(x, " "),
                          function(x) {
                            with(USPS, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                collapse = " "
                              )
                            })
                          }
                        )
                      }
                      
                      
                      f_TIC2 <- function(x) {
                        res <- c()
                        for (s in x) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, USPS$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                USPS,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        res
                      }
                      
                      
                      f_TIC3 <- function(x) {
                        x.split <- strsplit(x, " ")
                        lut <- with(USPS, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        )
                      }
                      
                      f_TIC4 <- function(x) {
                        xb <- gsub("^.*\\s+", "", x)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        paste0(gsub("\\w+$", "", x), replace(xb, !is.na(rp), na.omit(rp)))
                      }
                      
                      [1] "10900 harper ave"     "12235 davis anx"      "24 van cortland pkwy"
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly", 
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave", 
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou", 
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs", 
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard", 
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk", 
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass", 
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape", 
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center", 
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir", 
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf", 
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons", 
                       "cor", "corner", "corners", "cors", "course", "crse", "court", 
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk", 
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng", 
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam", 
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv", 
                       "drives", "est", "estate", "estates", "ests", "exp", "expr", 
                       "express", "expressway", "expw", "expy", "ext", "extension", 
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry", 
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats", 
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst", 
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks", 
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy", 
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns", 
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln", 
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves", 
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven", 
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway", 
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows", 
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands", 
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction", 
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky", 
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk", 
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane", 
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock", 
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops", 
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws", 
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn", 
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain", 
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck", 
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park", 
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky", 
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike", 
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains", 
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts", 
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad", 
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch", 
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg", 
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr", 
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl", 
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars", 
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng", 
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq", 
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station", 
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn", 
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt", 
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit", 
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce", 
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail", 
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel", 
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike", 
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally", 
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct", 
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg", 
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis", 
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy", 
                       "way", "ways", "well", "wells", "wls"), 
                       usps_abbrev = c("aly", 
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc", 
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu", 
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm", 
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br", 
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs", 
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn", 
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr", 
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir", 
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb", 
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse", 
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres", 
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd", 
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv", 
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests", 
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext", 
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry", 
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd", 
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs", 
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy", 
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns", 
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln", 
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr", 
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts", 
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls", 
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is", 
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct", 
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky", 
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk", 
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt", 
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg", 
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs", 
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml", 
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn", 
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch", 
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park", 
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass", 
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes", 
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt", 
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr", 
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch", 
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg", 
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd", 
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl", 
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs", 
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs", 
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta", 
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra", 
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st", 
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter", 
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak", 
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr", 
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke", 
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly", 
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws", 
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs", 
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk", 
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      USPS
                      
                      # A tibble: 503 x 2
                         common_abbrev usps_abbrev
                         <chr>         <chr>      
                       1 allee         aly        
                       2 alley         aly        
                       3 ally          aly        
                       4 aly           aly        
                       5 anex          anx        
                       6 annex         anx        
                       7 annx          anx        
                       8 anx           anx        
                       9 arc           arc        
                      10 arcade        arc        
                      # ... with 493 more rows
                      
                      USPSv = array(data = USPS$usps_abbrev, 
                                    dimnames= list(USPS$common_abbrev))
                      
                      USPSv['viadct']
                      # viadct 
                      #  "via" 
                      
                      USPSv['coves'] 
                      # coves 
                      # "cvs" 
                      
                      USPS_conv = function(x) {
                        comm = str_split(x, " ") %>% .[[1]] %>% .[length(.)]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv = Vectorize(USPS_conv)
                      
                      USPS_conv("10900 harper coves")
                      # 10900 harper coves 
                      # "10900 harper cvs"
                      
                      USPS_conv("10900 harper viadct")
                      # 10900 harper viadct 
                      # "10900 harper via"
                      
                      USPS_conv(c("10900 harper coves", "10900 harper viadct", "10900 harper ave"))
                      # 10900 harper coves 10900 harper viadct    10900 harper ave 
                      # "10900 harper cvs"  "10900 harper via"  "10900 harper ave"   
                      
                      n=10
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunnl   
                       3 7663 von brown wall
                       4 3043 harper lake   
                       5 9192 von brown grdn
                       6 120 marry rvr      
                       7 72 von brown locks 
                       8 8752 marry gardn   
                       9 7754 davis corner  
                      10 3745 davis jcts  
                      
                      df %>% mutate(addresses = USPS_conv(addresses))
                      
                      # A tibble: 10 x 1
                         addresses          
                         <chr>              
                       1 8995 davis crk     
                       2 8527 davis tunl    
                       3 7663 von brown wall
                       4 3043 harper lk     
                       5 9192 von brown gdn 
                       6 120 marry riv      
                       7 72 von brown lcks  
                       8 8752 marry gdn     
                       9 7754 davis cor     
                      10 3745 davis jcts 
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      
                      # A tibble: 1,000,000 x 1
                         addresses              
                         <chr>                  
                       1 8995 marry pass        
                       2 8527 davis spng        
                       3 7663 marry loaf        
                       4 3043 davis common      
                       5 9192 marry bnd         
                       6 120 von brown corner   
                       7 72 van cortland plains 
                       8 8752 van cortland crcle
                       9 7754 von brown sqrs    
                      10 3745 marry key         
                      # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df %>% mutate(addresses = USPS_conv(addresses))
                      Sys.time()-start_time
                      #Time difference of 3.610211 mins
                      
                      USPS_conv2 = function(x) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, USPSv[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2)
                      
                      #Simple Dictionary (hash Table) Interface for R
                      ht.create = function() new.env()
                      
                      ht.insert = function(ht, key, value)  ht[[key]] <- value
                      ht.insert = Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup = function(ht, key) ht[[key]]
                      ht.lookup = Vectorize(ht.lookup, "key")
                      
                      ht.delete = function(ht, key) rm(list=key,envir=ht,inherits=FALSE)
                      ht.delete = Vectorize(ht.delete, "key")
                      
                      ht1 = ht.create()
                      ht.insert(ht1, "a1", "va1" )
                      ht1 %>% ht.insert("a2", "va2")
                      ht.lookup(ht1, "a1")
                      # a1
                      # "va1"
                      ht1 %>% ht.lookup("a2")
                      # a2
                      # "va2"
                      
                      ht.insert(ht1, paste0("a", 1:10),paste0("va", 1:10))
                      ht1 %>% ht.insert( paste0("a", 11:20),paste0("va", 11:20))
                      
                      ht.lookup(ht1, paste0("a", 10:1))
                      # a10     a9     a8     a7     a6     a5     a4     a3     a2     a1
                      # "va10"  "va9"  "va8"  "va7"  "va6"  "va5"  "va4"  "va3"  "va2"  "va1"
                      ht1 %>% ht.lookup(paste0("a", 20:11))
                      # a20    a19    a18    a17    a16    a15    a14    a13    a12    a11
                      # "va20" "va19" "va18" "va17" "va16" "va15" "va14" "va13" "va12" "va11"
                      
                      #Functions that add a dictionary attribute to tibble
                      addHashTable = function(.data, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.data))) {
                          stop(paste0("`.data` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.data %>% distinct(!!key, !!value) %>% nrow)!=
                           (.data %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.data %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .data %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.data, "hashTab") = ht
                        .data
                      }
                      
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                      
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                      
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                      
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      USPS = USPS %>% addHashTable(common_abbrev, usps_abbrev)
                      str(USPS)
                      # tibble [503 x 2] (S3: tbl_df/tbl/data.frame)
                      # $ common_abbrev: chr [1:503] "allee" "alley" "ally" "aly" ...
                      # $ usps_abbrev  : chr [1:503] "aly" "aly" "aly" "aly" ...
                      # - attr(*, "hashTab")=<environment: 0x000000001591bbf0>
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                      
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                      
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                      
                        txt = .data %>% pull(!!value)
                        i = sapply(strsplit(txt, ""), function(x) max(which(x==" ")))
                        txt = paste0(str_sub(txt, end=i),
                                     ht.lookup(attr(.data, "hashTab"),
                                               str_sub(txt, start=i+1)))
                        .data %>% mutate(!!value := txt)
                      }
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      df
                      # # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry forges
                      # 2 787 von brown knol
                      # 3 2755 van cortland summit
                      # 4 9405 harper plaza
                      # 5 5376 marry pass
                      # 6 1857 marry trailer
                      # 7 9810 von brown drv
                      # 8 7984 davis garden
                      # 9 9110 marry alley
                      # 10 6458 von brown row
                      
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      df %>% replaceString(addresses)
                      # A tibble: 10 x 1
                      #   addresses
                      #   <chr>
                      # 1 74 marry frgs
                      # 2 787 von brown knl
                      # 3 2755 van cortland smt
                      # 4 9405 harper plz
                      # 5 5376 marry pass
                      # 6 1857 marry trlr
                      # 7 9810 von brown dr
                      # 8 7984 davis gdn
                      # 9 9110 marry aly
                      # 10 6458 von brown row
                      
                      start_time =Sys.time()
                      df = randomAddresses(1000000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      Sys.time()-start_time
                      #Time difference of 1.56609 secs
                      
                      df
                      # A tibble: 1,000,000 x 1
                      #   addresses              
                      #   <chr>                  
                      # 1 8995 marry pass        
                      # 2 8527 davis spng        
                      # 3 7663 marry loaf        
                      # 4 3043 davis common      
                      # 5 9192 marry bnd         
                      # 6 120 von brown corner   
                      # 7 72 van cortland plains 
                      # 8 8752 van cortland crcle
                      # 9 7754 von brown sqrs    
                      # 10 3745 marry key         
                      # # ... with 999,990 more rows
                      
                      start_time =Sys.time()
                      df = df %>% replaceString(addresses)
                      Sys.time()-start_time
                      #Time difference of 8.316476 secs
                      
                      # A tibble: 1,000,000 x 1
                      #   addresses            
                      #   <chr>                
                      #   1 8995 marry pass      
                      # 2 8527 davis spg       
                      # 3 7663 marry lf        
                      # 4 3043 davis cmn       
                      # 5 9192 marry bnd       
                      # 6 120 von brown cor    
                      # 7 72 van cortland plns 
                      # 8 8752 van cortland cir
                      # 9 7754 von brown sqs   
                      # 10 3745 marry ky        
                      # # ... with 999,990 more rows
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      df = df %>% addHashTable2(USPS, common_abbrev, usps_abbrev)
                      
                      library(microbenchmark)
                      mb1 = microbenchmark(
                        f_MK_conv2(df$addresses),
                        f_TIC1(df$addresses),
                        f_TIC2(df$addresses),
                        replaceString(df, addresses),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      ## Benchmarking with updated f_JM() and TIC4()
                      library(data.table)
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                                                 array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                        
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses, perl = TRUE)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses, perl = TRUE)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      #> # A tibble: 10 × 1
                      #>    addresses          
                      #>    <chr>              
                      #>  1 8995 davis crk     
                      #>  2 8527 davis tunl    
                      #>  3 7663 von brown wall
                      #>  4 3043 harper lk     
                      #>  5 9192 von brown gdn 
                      #>  6 120 marry riv      
                      #>  7 72 von brown lcks  
                      #>  8 8752 marry gdn     
                      #>  9 7754 davis cor     
                      #> 10 3745 davis jcts
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      #> Coordinate system already present. Adding new coordinate system, which will replace the existing one.
                      
                      library(tidyverse)
                      library(data.table)
                      
                      n=1000000
                      set.seed(1111)
                      df = tibble(
                        addresses = paste(
                          sample(10:10000, n, replace = TRUE),
                          sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                          sample(USPS$common_abbrev, n, replace = TRUE)
                        )
                      )
                      df
                      #> # A tibble: 1,000,000 × 1
                      #>    addresses              
                      #>    <chr>                  
                      #>  1 8995 marry pass        
                      #>  2 8527 davis spng        
                      #>  3 7663 marry loaf        
                      #>  4 3043 davis common      
                      #>  5 9192 marry bnd         
                      #>  6 120 von brown corner   
                      #>  7 72 van cortland plains 
                      #>  8 8752 van cortland crcle
                      #>  9 7754 von brown sqrs    
                      #> 10 3745 marry key         
                      #> # … with 999,990 more rows
                      
                      start_time =Sys.time()
                      df$abbreviation <- gsub("^.* ", "", df$addresses)
                      setDT(df)
                      setDT(USPS)
                      df[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                      
                      df$usps_abbreviation <- paste(str_extract(df$addresses, "^.* "), df$abbreviation, sep = "")
                      Sys.time()-start_time
                      #> Time difference of 2.804245 secs
                      df
                      #>                    addresses abbreviation usps_abbreviation
                      #>       1:     8995 marry pass         pass   8995 marry pass
                      #>       2:     8527 davis spng          spg    8527 davis spg
                      #>       3:     7663 marry loaf           lf     7663 marry lf
                      #>       4:   3043 davis common          cmn    3043 davis cmn
                      #>       5:      9192 marry bnd          bnd    9192 marry bnd
                      #>      ---                                                   
                      #>  999996:     1379 marry vdct          via    1379 marry via
                      #>  999997:    237 harper avnue          ave    237 harper ave
                      #>  999998:      7592 davis riv          riv    7592 davis riv
                      #>  999999: 4963 marry junction          jct    4963 marry jct
                      #> 1000000:     813 harper bluf          blf    813 harper blf
                      
                      set.seed(1111)
                      df <- randomAddresses(10000)
                      
                      dt_func <- function(x) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(USPS)
                        x[USPS, abbreviation:=usps_abbrev, on=.(abbreviation=common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      
                      df2 <- f_MK_replaceString(df, addresses)
                      df3 <- dt_func(df)
                      dplyr::all_equal(df2, df3)
                      #> [1] TRUE
                      
                      library(tidyverse)
                      library(data.table)
                      
                      library(tidyverse)
                      USPS = tibble(
                       common_abbrev = c("allee", "alley", "ally", "aly",
                       "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                       "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                       "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                       "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                       "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                       "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                       "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                       "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                       "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                       "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                       "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                       "cor", "corner", "corners", "cors", "course", "crse", "court",
                       "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                       "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                       "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                       "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                       "drives", "est", "estate", "estates", "ests", "exp", "expr",
                       "express", "expressway", "expw", "expy", "ext", "extension",
                       "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                       "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                       "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                       "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                       "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                       "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                       "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                       "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                       "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                       "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                       "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                       "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                       "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                       "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                       "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                       "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                       "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                       "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                       "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                       "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                       "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                       "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                       "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                       "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                       "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                       "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                       "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                       "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                       "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                       "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                       "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                       "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                       "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                       "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                       "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                       "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                       "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                       "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                       "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                       "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                       "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                       "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                       "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                       "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                       "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                       "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                       "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                       "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                       "way", "ways", "well", "wells", "wls"),
                       usps_abbrev = c("aly",
                       "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                       "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                       "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                       "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                       "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                       "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                       "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                       "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                       "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                       "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                       "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                       "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                       "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                       "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                       "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                       "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                       "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                       "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                       "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                       "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                       "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                       "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                       "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                       "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                       "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                       "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                       "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                       "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                       "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                       "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                       "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                       "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                       "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                       "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                       "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                       "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                       "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                       "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                       "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                       "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                       "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                       "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                       "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                       "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                       "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                       "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                       "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                       "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                       "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                       "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                       "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                       "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                       "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                       "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                       "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                       "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                       "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = paste(
                            sample(10:10000, n, replace = TRUE),
                            sample(c("harper", "davis", "van cortland", "marry", "von brown"), n, replace = TRUE),
                            sample(USPS$common_abbrev, n, replace = TRUE)
                          )
                        )
                      }
                      
                      set.seed(1111)
                      df = randomAddresses(10)
                      
                      USPS_conv2 = function(x, y) {
                        t = str_split(x, " ")
                        comm = t[[1]][length(t[[1]])]
                        str_replace(x, comm, y[comm])
                      }
                      USPS_conv2 = Vectorize(USPS_conv2, "x")
                      
                      f_MK_conv2 <- function(x, y) {
                        x %>% mutate(
                          addresses = USPS_conv2(addresses, 
                            array(data = y$usps_abbrev, dimnames = list(y$common_abbrev))))
                      }
                      f_MK_conv2(df, USPS)
                      
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      ht.delete <- function(ht, key) rm(list = key, envir = ht, inherits = FALSE)
                      ht.delete <- Vectorize(ht.delete, "key")
                      
                      
                      f_MK_replaceString <- function(x, y) {
                        ht <- ht.create()
                        ht.insert(ht, y$common_abbrev, y$usps_abbrev)
                      
                        txt <- x$addresses
                        i <- sapply(strsplit(txt, ""), function(x) max(which(x == " ")))
                        txt <- paste0(
                          str_sub(txt, end = i),
                          ht.lookup(ht, str_sub(txt, start = i + 1))
                        )
                        x %>% mutate(addresses = txt)
                      }
                      f_MK_replaceString(df, USPS)
                      
                      f_TIC1 <- function(x, y) {
                        x %>% mutate(addresses = sapply(
                          strsplit(x$addresses, " "),
                          function(x) {
                            with(y, {
                              idx <- match(x, common_abbrev)
                              paste0(ifelse(is.na(idx), x, usps_abbrev[idx]),
                                     collapse = " "
                              )
                            })
                          }
                        )
                        )
                      }
                      f_TIC1(df, USPS)
                      
                      
                      f_TIC2 <- function(x, y) {
                        res <- c()
                        for (s in x$addresses) {
                          v <- unlist(strsplit(s, "\\W+"))
                          for (p in v) {
                            k <- match(p, y$common_abbrev)
                            if (!is.na(k)) {
                              s <- with(
                                y,
                                gsub(
                                  sprintf("\\b%s\\b", common_abbrev[k]),
                                  usps_abbrev[k],
                                  s
                                )
                              )
                            }
                          }
                          res <- append(res, s)
                        }
                        x %>% mutate(addresses = res)
                      }
                      f_TIC2(df, USPS)
                      
                      
                      f_TIC3 <- function(x, y) {
                        x.split <- strsplit(x$addresses, " ")
                        lut <- with(y, setNames(usps_abbrev, common_abbrev))
                        grp <- rep(seq_along(x.split), lengths(x.split))
                        xx <- unlist(x.split)
                        r <- lut[xx]
                        x %>% mutate(addresses = tapply(
                          replace(xx, !is.na(r), na.omit(r)),
                          grp,
                          function(s) paste0(s, collapse = " ")
                        ))
                      }
                      f_TIC3(df, USPS)
                      
                      f_TIC4 <- function(x, y) {
                        xb <- gsub("^.*\\s+", "", x$addresses)
                        rp <- with(USPS, usps_abbrev[match(xb, common_abbrev)])
                        x %>% mutate(addresses = paste0(gsub("\\w+$", "", x$addresses), replace(xb, !is.na(rp), na.omit(rp))))
                      }
                      f_TIC4(df, USPS)
                      
                      f_JM <- function(x, y) {
                        x$abbreviation <- gsub("^.* ", "", x$addresses)
                        setDT(x)
                        setDT(y)
                        x[y, abbreviation := usps_abbrev, on = .(abbreviation = common_abbrev)]
                        
                        x$addresses <- paste(str_extract(x$addresses, "^.* "), x$abbreviation, sep = "")
                        x$abbreviation <- NULL
                        return(as_tibble(x))
                      }
                      f_JM(df, USPS)
                      
                      set.seed(1111)
                      df = randomAddresses(100)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(1000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(10000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_conv2(df, USPS),
                        f_MK_replaceString(df, USPS),
                        f_TIC1(df, USPS),
                        f_TIC2(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(100000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC3(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      set.seed(1111)
                      df = randomAddresses(1000000)
                      
                      library(microbenchmark)
                      mb1 <- microbenchmark(
                        f_MK_replaceString(df, USPS),
                        f_TIC4(df, USPS),
                        f_JM(df, USPS),
                        times = 20L
                      )
                      ggplot2::autoplot(mb1)
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      
                      library(tidyverse)
                      
                      USPS = tibble(
                        common_abbrev = c("allee", "alley", "ally", "aly",
                                          "anex", "annex", "annx", "anx", "arc", "arcade", "av", "ave",
                                          "aven", "avenu", "avenue", "avn", "avnue", "bayoo", "bayou",
                                          "bch", "beach", "bend", "bnd", "blf", "bluf", "bluff", "bluffs",
                                          "bot", "btm", "bottm", "bottom", "blvd", "boul", "boulevard",
                                          "boulv", "br", "brnch", "branch", "brdge", "brg", "bridge", "brk",
                                          "brook", "brooks", "burg", "burgs", "byp", "bypa", "bypas", "bypass",
                                          "byps", "camp", "cp", "cmp", "canyn", "canyon", "cnyn", "cape",
                                          "cpe", "causeway", "causwa", "cswy", "cen", "cent", "center",
                                          "centr", "centre", "cnter", "cntr", "ctr", "centers", "cir",
                                          "circ", "circl", "circle", "crcl", "crcle", "circles", "clf",
                                          "cliff", "clfs", "cliffs", "clb", "club", "common", "commons",
                                          "cor", "corner", "corners", "cors", "course", "crse", "court",
                                          "ct", "courts", "cts", "cove", "cv", "coves", "creek", "crk",
                                          "crescent", "cres", "crsent", "crsnt", "crest", "crossing", "crssng",
                                          "xing", "crossroad", "crossroads", "curve", "dale", "dl", "dam",
                                          "dm", "div", "divide", "dv", "dvd", "dr", "driv", "drive", "drv",
                                          "drives", "est", "estate", "estates", "ests", "exp", "expr",
                                          "express", "expressway", "expw", "expy", "ext", "extension",
                                          "extn", "extnsn", "exts", "fall", "falls", "fls", "ferry", "frry",
                                          "fry", "field", "fld", "fields", "flds", "flat", "flt", "flats",
                                          "flts", "ford", "frd", "fords", "forest", "forests", "frst",
                                          "forg", "forge", "frg", "forges", "fork", "frk", "forks", "frks",
                                          "fort", "frt", "ft", "freeway", "freewy", "frway", "frwy", "fwy",
                                          "garden", "gardn", "grden", "grdn", "gardens", "gdns", "grdns",
                                          "gateway", "gatewy", "gatway", "gtway", "gtwy", "glen", "gln",
                                          "glens", "green", "grn", "greens", "grov", "grove", "grv", "groves",
                                          "harb", "harbor", "harbr", "hbr", "hrbor", "harbors", "haven",
                                          "hvn", "ht", "hts", "highway", "highwy", "hiway", "hiwy", "hway",
                                          "hwy", "hill", "hl", "hills", "hls", "hllw", "hollow", "hollows",
                                          "holw", "holws", "inlt", "is", "island", "islnd", "islands",
                                          "islnds", "iss", "isle", "isles", "jct", "jction", "jctn", "junction",
                                          "junctn", "juncton", "jctns", "jcts", "junctions", "key", "ky",
                                          "keys", "kys", "knl", "knol", "knoll", "knls", "knolls", "lk",
                                          "lake", "lks", "lakes", "land", "landing", "lndg", "lndng", "lane",
                                          "ln", "lgt", "light", "lights", "lf", "loaf", "lck", "lock",
                                          "lcks", "locks", "ldg", "ldge", "lodg", "lodge", "loop", "loops",
                                          "mall", "mnr", "manor", "manors", "mnrs", "meadow", "mdw", "mdws",
                                          "meadows", "medows", "mews", "mill", "mills", "missn", "mssn",
                                          "motorway", "mnt", "mt", "mount", "mntain", "mntn", "mountain",
                                          "mountin", "mtin", "mtn", "mntns", "mountains", "nck", "neck",
                                          "orch", "orchard", "orchrd", "oval", "ovl", "overpass", "park",
                                          "prk", "parks", "parkway", "parkwy", "pkway", "pkwy", "pky",
                                          "parkways", "pkwys", "pass", "passage", "path", "paths", "pike",
                                          "pikes", "pine", "pines", "pnes", "pl", "plain", "pln", "plains",
                                          "plns", "plaza", "plz", "plza", "point", "pt", "points", "pts",
                                          "port", "prt", "ports", "prts", "pr", "prairie", "prr", "rad",
                                          "radial", "radiel", "radl", "ramp", "ranch", "ranches", "rnch",
                                          "rnchs", "rapid", "rpd", "rapids", "rpds", "rest", "rst", "rdg",
                                          "rdge", "ridge", "rdgs", "ridges", "riv", "river", "rvr", "rivr",
                                          "rd", "road", "roads", "rds", "route", "row", "rue", "run", "shl",
                                          "shoal", "shls", "shoals", "shoar", "shore", "shr", "shoars",
                                          "shores", "shrs", "skyway", "spg", "spng", "spring", "sprng",
                                          "spgs", "spngs", "springs", "sprngs", "spur", "spurs", "sq",
                                          "sqr", "sqre", "squ", "square", "sqrs", "squares", "sta", "station",
                                          "statn", "stn", "stra", "strav", "straven", "stravenue", "stravn",
                                          "strvn", "strvnue", "stream", "streme", "strm", "street", "strt",
                                          "st", "str", "streets", "smt", "suite", "sumit", "sumitt", "summit",
                                          "ter", "terr", "terrace", "throughway", "trace", "traces", "trce",
                                          "track", "tracks", "trak", "trk", "trks", "trafficway", "trail",
                                          "trails", "trl", "trls", "trailer", "trlr", "trlrs", "tunel",
                                          "tunl", "tunls", "tunnel", "tunnels", "tunnl", "trnpk", "turnpike",
                                          "turnpk", "underpass", "un", "union", "unions", "valley", "vally",
                                          "vlly", "vly", "valleys", "vlys", "vdct", "via", "viadct", "viaduct",
                                          "view", "vw", "views", "vws", "vill", "villag", "village", "villg",
                                          "villiage", "vlg", "villages", "vlgs", "ville", "vl", "vis",
                                          "vist", "vista", "vst", "vsta", "walk", "walks", "wall", "wy",
                                          "way", "ways", "well", "wells", "wls"),
                        usps_abbrev = c("aly",
                                        "aly", "aly", "aly", "anx", "anx", "anx", "anx", "arc", "arc",
                                        "ave", "ave", "ave", "ave", "ave", "ave", "ave", "byu", "byu",
                                        "bch", "bch", "bnd", "bnd", "blf", "blf", "blf", "blfs", "btm",
                                        "btm", "btm", "btm", "blvd", "blvd", "blvd", "blvd", "br", "br",
                                        "br", "brg", "brg", "brg", "brk", "brk", "brks", "bg", "bgs",
                                        "byp", "byp", "byp", "byp", "byp", "cp", "cp", "cp", "cyn", "cyn",
                                        "cyn", "cpe", "cpe", "cswy", "cswy", "cswy", "ctr", "ctr", "ctr",
                                        "ctr", "ctr", "ctr", "ctr", "ctr", "ctrs", "cir", "cir", "cir",
                                        "cir", "cir", "cir", "cirs", "clf", "clf", "clfs", "clfs", "clb",
                                        "clb", "cmn", "cmns", "cor", "cor", "cors", "cors", "crse", "crse",
                                        "ct", "ct", "cts", "cts", "cv", "cv", "cvs", "crk", "crk", "cres",
                                        "cres", "cres", "cres", "crst", "xing", "xing", "xing", "xrd",
                                        "xrds", "curv", "dl", "dl", "dm", "dm", "dv", "dv", "dv", "dv",
                                        "dr", "dr", "dr", "dr", "drs", "est", "est", "ests", "ests",
                                        "expy", "expy", "expy", "expy", "expy", "expy", "ext", "ext",
                                        "ext", "ext", "exts", "fall", "fls", "fls", "fry", "fry", "fry",
                                        "fld", "fld", "flds", "flds", "flt", "flt", "flts", "flts", "frd",
                                        "frd", "frds", "frst", "frst", "frst", "frg", "frg", "frg", "frgs",
                                        "frk", "frk", "frks", "frks", "ft", "ft", "ft", "fwy", "fwy",
                                        "fwy", "fwy", "fwy", "gdn", "gdn", "gdn", "gdn", "gdns", "gdns",
                                        "gdns", "gtwy", "gtwy", "gtwy", "gtwy", "gtwy", "gln", "gln",
                                        "glns", "grn", "grn", "grns", "grv", "grv", "grv", "grvs", "hbr",
                                        "hbr", "hbr", "hbr", "hbr", "hbrs", "hvn", "hvn", "hts", "hts",
                                        "hwy", "hwy", "hwy", "hwy", "hwy", "hwy", "hl", "hl", "hls",
                                        "hls", "holw", "holw", "holw", "holw", "holw", "inlt", "is",
                                        "is", "is", "iss", "iss", "iss", "isle", "isle", "jct", "jct",
                                        "jct", "jct", "jct", "jct", "jcts", "jcts", "jcts", "ky", "ky",
                                        "kys", "kys", "knl", "knl", "knl", "knls", "knls", "lk", "lk",
                                        "lks", "lks", "land", "lndg", "lndg", "lndg", "ln", "ln", "lgt",
                                        "lgt", "lgts", "lf", "lf", "lck", "lck", "lcks", "lcks", "ldg",
                                        "ldg", "ldg", "ldg", "loop", "loop", "mall", "mnr", "mnr", "mnrs",
                                        "mnrs", "mdw", "mdws", "mdws", "mdws", "mdws", "mews", "ml",
                                        "mls", "msn", "msn", "mtwy", "mt", "mt", "mt", "mtn", "mtn",
                                        "mtn", "mtn", "mtn", "mtn", "mtns", "mtns", "nck", "nck", "orch",
                                        "orch", "orch", "oval", "oval", "opas", "park", "park", "park",
                                        "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pkwy", "pass",
                                        "psge", "path", "path", "pike", "pike", "pne", "pnes", "pnes",
                                        "pl", "pln", "pln", "plns", "plns", "plz", "plz", "plz", "pt",
                                        "pt", "pts", "pts", "prt", "prt", "prts", "prts", "pr", "pr",
                                        "pr", "radl", "radl", "radl", "radl", "ramp", "rnch", "rnch",
                                        "rnch", "rnch", "rpd", "rpd", "rpds", "rpds", "rst", "rst", "rdg",
                                        "rdg", "rdg", "rdgs", "rdgs", "riv", "riv", "riv", "riv", "rd",
                                        "rd", "rds", "rds", "rte", "row", "rue", "run", "shl", "shl",
                                        "shls", "shls", "shr", "shr", "shr", "shrs", "shrs", "shrs",
                                        "skwy", "spg", "spg", "spg", "spg", "spgs", "spgs", "spgs", "spgs",
                                        "spur", "spur", "sq", "sq", "sq", "sq", "sq", "sqs", "sqs", "sta",
                                        "sta", "sta", "sta", "stra", "stra", "stra", "stra", "stra",
                                        "stra", "stra", "strm", "strm", "strm", "st", "st", "st", "st",
                                        "sts", "smt", "ste", "smt", "smt", "smt", "ter", "ter", "ter",
                                        "trwy", "trce", "trce", "trce", "trak", "trak", "trak", "trak",
                                        "trak", "trfy", "trl", "trl", "trl", "trl", "trlr", "trlr", "trlr",
                                        "tunl", "tunl", "tunl", "tunl", "tunl", "tunl", "tpke", "tpke",
                                        "tpke", "upas", "un", "un", "uns", "vly", "vly", "vly", "vly",
                                        "vlys", "vlys", "via", "via", "via", "via", "vw", "vw", "vws",
                                        "vws", "vlg", "vlg", "vlg", "vlg", "vlg", "vlg", "vlgs", "vlgs",
                                        "vl", "vl", "vis", "vis", "vis", "vis", "vis", "walk", "walk",
                                        "wall", "way", "way", "ways", "wl", "wls", "wls"))
                      
                      
                      randomAddresses = function(n){
                        tibble(
                          addresses = 
                            replicate(
                              n, 
                              sample(c(sample(10:10000, 1, replace = TRUE) %>% paste0,
                                       sample(c("harper", "davis", "van cortland", "marry", "von brown"), 1),
                                       sample(USPS$common_abbrev, 1)), 3) %>% paste(collapse = " ")
                            )
                        )
                      }
                      
                      ht.create <- function() new.env()
                      
                      ht.insert <- function(ht, key, value) ht[[key]] <- value
                      ht.insert <- Vectorize(ht.insert, c("key", "value"))
                      
                      ht.lookup <- function(ht, key) ht[[key]]
                      ht.lookup <- Vectorize(ht.lookup, "key")
                      
                      addHashTable2 = function(.x, .y, key, value){
                        key = enquo(key)
                        value = enquo(value)
                        
                        if (!all(c(as_label(key), as_label(value)) %in% names(.y))) {
                          stop(paste0("`.y` must contain `", as_label(key),
                                      "` and `", as_label(value), "` columns"))
                        }
                        
                        if((.y %>% distinct(!!key, !!value) %>% nrow)!=
                           (.y %>% distinct(!!key) %>% nrow)){
                          warning(paste0(
                            "\nThe number of unique values of the ", as_label(key),
                            " variable is different\n",
                            " from the number of unique values of the ",
                            as_label(key), " and ", as_label(value)," pairs!\n",
                            "The dictionary will only return the last values for a given key!"))
                        }
                        
                        ht = ht.create()
                        ht %>% ht.insert(.y %>% distinct(!!key, !!value) %>% pull(!!key),
                                         .y %>% distinct(!!key, !!value) %>% pull(!!value))
                        attr(.x, "hashTab") = ht
                        .x
                      }
                      
                      replaceString = function(.data, value){
                        value = enquo(value)
                        
                        #Test whether the value variable is in .data
                        if(!(as_label(value) %in% names(.data))){
                          stop(paste("The", as_label(value),
                                     "variable does not exist in the .data table!"))
                        }
                        
                        #Dictionary attribute presence test
                        if(!("hashTab" %in% names(attributes(.data)))) {
                          stop(paste0(
                            "\nThere is no dictionary attribute in the .data table!\n",
                            "Use addHashTable or addHashTable2 to add a dictionary attribute."))
                        }
                        
                        ht = attr(.data, "hashTab")
                        txtRep = function(txt){
                          txt = str_split(txt, " ")[[1]]
                          httxt = ht.lookup(ht, txt)
                          txt[httxt!="NULL"] = httxt[httxt!="NULL"]
                          paste(txt, collapse = " ")
                        }
                        .data %>% rowwise(!!value) %>%  
                          mutate(!!value := txtRep(!!value))
                      }
                      
                      set.seed(1111)
                      df=randomAddresses(10)
                      df
                      
                      # A tibble: 10 x 1
                         addresses             
                         <chr>                 
                       1 marry wall 8995       
                       2 cen 9192 marry        
                       3 bayoo 3745 davis      
                       4 marry hollows 4104    
                       5 grdn 7162 marry       
                       6 lck harper 1211       
                       7 9405 van cortland knol
                       8 7984 von brown viadct 
                       9 4365 von brown rue    
                      10 6399 von brown mssn 
                      
                      df %>% addHashTable2(USPS, common_abbrev, usps_abbrev) %>% 
                        replaceString(addresses)
                      
                      # A tibble: 10 x 1
                      # Rowwise:  addresses
                         addresses            
                         <chr>                
                       1 marry wall 8995      
                       2 ctr 9192 marry       
                       3 byu 3745 davis       
                       4 marry holw 4104      
                       5 gdn 7162 marry       
                       6 lck harper 1211      
                       7 9405 van cortland knl
                       8 7984 von brown via   
                       9 4365 von brown rue   
                      10 6399 von brown msn  
                      

                      How to Position Text and Icons on Top of a Video in html

                      copy iconCopydownload iconDownload
                      .hero {
                          position: relative;
                          height: 500px;
                      }
                      
                      .hero-title {
                          z-index: 20;
                          color: white;
                          text-align: center;
                          margin-top: 50px;
                          text-shadow: 0px 0px 7px rgb(0, 0, 0);
                      }
                      
                      .hero-btm-area {
                          margin-bottom: 50px;
                          z-index: 20;
                          text-shadow: 0px 0px 7px rgb(0, 0, 0);
                      }
                      
                      .hero-icon {
                          width: 40px;
                          margin-bottom: 10px;
                      }
                      #vid {
                        position: absolute;
                        top: 0; left: 0;
                        object-fit: cover;
                        z-index: -1;
                      }
                      <video width="1920" height="1000" autoplay muted loop playsinline id="vid">
                          <source src="https://www.w3schools.com/tags/movie.mp4" type="video/mp4">
                          Your browser does not support the video tag.
                      </video>
                      <script>
                          document.getElementById('vid').play();
                      </script>
                      <link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
                      <div class="hero">
                        <div class="container h-100 d-flex flex-column">
                          <div class="row">
                            <div class="col">
                              <div class="hero-title">
                                <h1>Hero Title</h1>
                              </div>
                            </div>
                          </div>
                          <div class="row mt-auto hero-btm-area">
                            <div class="col text-white text-center border-right border-white">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature One
                            </div>
                            <div class="col text-white text-center border-right border-white">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature Two
                            </div>
                            <div class="col text-white text-center">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature Three
                            </div>
                          </div>
                        </div>
                        <div class="hero-bg-img">
                        </div>
                      </div>
                      .hero {
                          position: relative;
                          height: 500px;
                      }
                      
                      .hero-title {
                          z-index: 20;
                          color: white;
                          text-align: center;
                          margin-top: 50px;
                          text-shadow: 0px 0px 7px rgb(0, 0, 0);
                      }
                      
                      .hero-btm-area {
                          margin-bottom: 50px;
                          z-index: 20;
                          text-shadow: 0px 0px 7px rgb(0, 0, 0);
                      }
                      
                      .hero-icon {
                          width: 40px;
                          margin-bottom: 10px;
                      }
                      #vid {
                        position: absolute;
                        top: 0; left: 0;
                        object-fit: cover;
                        z-index: -1;
                      }
                      <video width="1920" height="1000" autoplay muted loop playsinline id="vid">
                          <source src="https://www.w3schools.com/tags/movie.mp4" type="video/mp4">
                          Your browser does not support the video tag.
                      </video>
                      <script>
                          document.getElementById('vid').play();
                      </script>
                      <link href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
                      <div class="hero">
                        <div class="container h-100 d-flex flex-column">
                          <div class="row">
                            <div class="col">
                              <div class="hero-title">
                                <h1>Hero Title</h1>
                              </div>
                            </div>
                          </div>
                          <div class="row mt-auto hero-btm-area">
                            <div class="col text-white text-center border-right border-white">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature One
                            </div>
                            <div class="col text-white text-center border-right border-white">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature Two
                            </div>
                            <div class="col text-white text-center">
                              <div class="hero-icon mx-auto">
                                **Icon**
                              </div>
                              Feature Three
                            </div>
                          </div>
                        </div>
                        <div class="hero-bg-img">
                        </div>
                      </div>

                      How to update state in React

                      copy iconCopydownload iconDownload
                       import React from 'react';
                      
                      export class Hamburger extends React.Component {
                          constructor (props) {
                              super(props);
                              this.state = {
                                  click: false
                              };
                          }
                      
                          handleClick = () => {
                              this.setState({click: !click});
                          }
                          render() {
                              let className = 'hamburger';
                              if (this.state.click) {
                                  className += ' cross';
                              }
                      
                              return (
                                  <div
                                      className={className}
                                      onclick={this.handleClick}
                                  >
                                      <svg
                                          viewbox='0 0 100 100'
                                          preserveAspectRatio='xMidYMid meet'
                                      >
                                          <line x1='10' x2='90' y1='20' y2='20' id='top'/>
                                          <line x1='10' x2='90' y1='50' y2='50' id='mid'/>
                                          <line x1='10' x2='90' y1='80' y2='80' id='btm'/>
                                      </svg>
                                  </div>
                              );
                          }
                      }
                      

                      want to match email and password from firestore database in js

                      copy iconCopydownload iconDownload
                      const docRef = db.collection('Companies List')
                         .where("CompanyEmail", "==", useremail)
                         .where("CompanyPassword", "==", password);
                      // ^ separate .where()
                      
                      docRef.get().then((snapshot) => {
                        if (snapshot.empty) {
                          // no docs matched
                          console.log("Invalid Credentials")
                        } else {
                          console.log(snapshot.docs[0].data())
                          Swal.fire({
                            title:'Match!',
                            text: 'Welcome to the dashboard',
                            type: 'success',
                          }).then(function() {
                            window.location = "dashboard.html";
                          });
                        }
                      })
                      

                      How to align these four div elements with images side by side

                      copy iconCopydownload iconDownload
                      body,
                      h1,
                      h2,
                      h3,
                      h4,
                      h5,
                      h6 {
                        font-family: "Raleway", sans-serif;
                        color: black;
                      }
                      
                      .hdr-logos {
                        display: block;
                        font-size: 30px;
                        color: #86fce1;
                        background-color: black;
                        width: 16%;
                        border-radius: 20px;
                        text-align: center;
                        margin: auto;
                      }
                      
                      body,
                      html {
                        height: 100%;
                        line-height: 1.8;
                        margin: 0px;
                        overflow: auto;
                      }
                      
                      
                      /* Full height image header */
                      
                      .bgimg-1 {
                        background-position: center;
                        background-size: cover;
                        background-image: url("https://d34wrbuj7u1x6f.cloudfront.net/image/cache/data/Art%202017+/2744web-900xVariable.1.jpg");
                        min-height: 100%;
                      }
                      
                      .navbar {
                        background-color: black;
                        top: 0px;
                      }
                      
                      .website-logo {
                        float: left;
                        padding: 15px;
                        text-decoration: none;
                        color: white;
                      }
                      
                      .rsnbl {
                        float: center;
                        color: #f2f2f2;
                        text-align: center;
                        padding: 14px 16px;
                        font-size: 17px;
                      }
                      
                      .rsnbl a {
                        text-decoration: none;
                        color: white;
                        padding: 20px;
                        margin-right: 40px;
                        transition: 0.5s;
                      }
                      
                      .rsnbl a:hover {
                        background-color: #6b3f12;
                        padding: 10px 100px 10px 100px;
                        transition: 0.5s;
                        transition-width: 300px;
                      }
                      
                      .spanthings {
                        text-align: center;
                        color: white;
                      }
                      
                      #top-span {
                        font-size: 50px;
                        color: white;
                        padding: 5px 20px;
                        border: solid, rgb(0, 0, 0, 0.6);
                        background-color: rgb(0, 0, 0, 0.8);
                        border-radius: 20px;
                      }
                      
                      .abt-link1 {
                        text-decoration: none;
                        color: white;
                        border: 1px solid;
                        border-radius: 20px;
                        padding: 10px;
                        background-color: rgb(0, 0, 0, 0.6);
                      }
                      
                      #btm-span {
                        color: white;
                        font-size: 25px;
                        background-color: rgb(0, 0, 0, 0.5);
                      }
                      
                      .abt-link1:hover {
                        background-color: rgb(0, 0, 0);
                        padding: 12px;
                        transition: 0.6s;
                      }
                      
                      .t1container {
                        text-align: center;
                      }
                      
                      .acontainer {
                        max-width: 25vw;
                        text-align: center;
                        float: left;
                      }
                      
                      .acontainer img {
                        border-radius: 50%;
                        max-width: 100vw;
                      }
                      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
                      
                      <div class="navbar" id="myNavbar">
                        <a href="#home" class="website-logo">LOGO</a>
                        <!-- Right-sided navbar links -->
                        <div class="rsnbl">
                          <a href="#about">ABOUT</a>
                          <a href="#team"><i class="fa fa-user"></i> TEAM</a>
                          <a href="#work"><i class="fa fa-th"></i> WORK</a>
                          <a href="#pricing"><i class="fa fa-usd"></i> PRICING</a>
                          <a href="#contact"><i class="fa fa-envelope"></i> CONTACT</a>
                        </div>
                      </div>
                      
                      <header class="bgimg-1" id="home">
                        <div class="spanthings">
                          <span id="top-span">Supporting Culture</span><br>
                          <span id="btm-span">Thrive within your identity</span>
                          <p><a href="#about" class="abt-link1" style="transition:0.6s">Learn more and enquire today</a></p>
                        </div>
                        <div class="hdr-logos">
                          <i class="fa fa-facebook-official"></i>
                          <i class="fa fa-instagram"></i>
                          <i class="fa fa-snapchat"></i>
                          <i class="fa fa-pinterest-p"></i>
                          <i class="fa fa-twitter"></i>
                          <i class="fa fa-linkedin"></i>
                        </div>
                      </header>
                      
                      <!-- About Section -->
                      <div class="container" style="padding:128px 16px" id="about">
                        <div class="t1container">
                          <h3>ABOUT THE COMPANY</h3>
                          <p>Key features of our company</p>
                        </div>
                        <div style="margin-top:64px">
                          <div class="acontainer">
                            <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTcMmREXrI8ss0rmMnZWKI7L5lL23vbnb3YGQ&usqp=CAU" height="200" width="200" class="abtusimgcul" />
                            <p style="font-size:40px">Culture</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                          <div class="acontainer">
                            <img src="https://artsnorthernrivers.com.au/wp-content/uploads/KateHolmes_ArtsNR_ArtOnBundjalungMkt_2019_49.jpg" class="abtusimgpas" height="200" width="200" />
                            <p style="font-size:40px">Passion</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                          <div class="acontainer">
                            <img src="https://artsnorthernrivers.com.au/wp-content/uploads/2021/02/ANR_Weaving-Book-_Web-Header.jpg" height="200" width="200" class="abtusimgsup" />
                            <p style="font-size:40px">Support</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                        </div>
                      body,
                      h1,
                      h2,
                      h3,
                      h4,
                      h5,
                      h6 {
                        font-family: "Raleway", sans-serif;
                        color: black;
                      }
                      
                      .hdr-logos {
                        display: block;
                        font-size: 30px;
                        color: #86fce1;
                        background-color: black;
                        width: 16%;
                        border-radius: 20px;
                        text-align: center;
                        margin: auto;
                      }
                      
                      body,
                      html {
                        height: 100%;
                        line-height: 1.8;
                        margin: 0px;
                        overflow: auto;
                      }
                      
                      
                      /* Full height image header */
                      
                      .bgimg-1 {
                        background-position: center;
                        background-size: cover;
                        background-image: url("https://d34wrbuj7u1x6f.cloudfront.net/image/cache/data/Art%202017+/2744web-900xVariable.1.jpg");
                        min-height: 100%;
                      }
                      
                      .navbar {
                        background-color: black;
                        top: 0px;
                      }
                      
                      .website-logo {
                        float: left;
                        padding: 15px;
                        text-decoration: none;
                        color: white;
                      }
                      
                      .rsnbl {
                        float: center;
                        color: #f2f2f2;
                        text-align: center;
                        padding: 14px 16px;
                        font-size: 17px;
                      }
                      
                      .rsnbl a {
                        text-decoration: none;
                        color: white;
                        padding: 20px;
                        margin-right: 40px;
                        transition: 0.5s;
                      }
                      
                      .rsnbl a:hover {
                        background-color: #6b3f12;
                        padding: 10px 100px 10px 100px;
                        transition: 0.5s;
                        transition-width: 300px;
                      }
                      
                      .spanthings {
                        text-align: center;
                        color: white;
                      }
                      
                      #top-span {
                        font-size: 50px;
                        color: white;
                        padding: 5px 20px;
                        border: solid, rgb(0, 0, 0, 0.6);
                        background-color: rgb(0, 0, 0, 0.8);
                        border-radius: 20px;
                      }
                      
                      .abt-link1 {
                        text-decoration: none;
                        color: white;
                        border: 1px solid;
                        border-radius: 20px;
                        padding: 10px;
                        background-color: rgb(0, 0, 0, 0.6);
                      }
                      
                      #btm-span {
                        color: white;
                        font-size: 25px;
                        background-color: rgb(0, 0, 0, 0.5);
                      }
                      
                      .abt-link1:hover {
                        background-color: rgb(0, 0, 0);
                        padding: 12px;
                        transition: 0.6s;
                      }
                      
                      .t1container {
                        text-align: center;
                      }
                      
                      .acontainer {
                        max-width: 25vw;
                        text-align: center;
                        float: left;
                      }
                      
                      .acontainer img {
                        border-radius: 50%;
                        max-width: 100vw;
                      }
                      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
                      
                      <div class="navbar" id="myNavbar">
                        <a href="#home" class="website-logo">LOGO</a>
                        <!-- Right-sided navbar links -->
                        <div class="rsnbl">
                          <a href="#about">ABOUT</a>
                          <a href="#team"><i class="fa fa-user"></i> TEAM</a>
                          <a href="#work"><i class="fa fa-th"></i> WORK</a>
                          <a href="#pricing"><i class="fa fa-usd"></i> PRICING</a>
                          <a href="#contact"><i class="fa fa-envelope"></i> CONTACT</a>
                        </div>
                      </div>
                      
                      <header class="bgimg-1" id="home">
                        <div class="spanthings">
                          <span id="top-span">Supporting Culture</span><br>
                          <span id="btm-span">Thrive within your identity</span>
                          <p><a href="#about" class="abt-link1" style="transition:0.6s">Learn more and enquire today</a></p>
                        </div>
                        <div class="hdr-logos">
                          <i class="fa fa-facebook-official"></i>
                          <i class="fa fa-instagram"></i>
                          <i class="fa fa-snapchat"></i>
                          <i class="fa fa-pinterest-p"></i>
                          <i class="fa fa-twitter"></i>
                          <i class="fa fa-linkedin"></i>
                        </div>
                      </header>
                      
                      <!-- About Section -->
                      <div class="container" style="padding:128px 16px" id="about">
                        <div class="t1container">
                          <h3>ABOUT THE COMPANY</h3>
                          <p>Key features of our company</p>
                        </div>
                        <div style="margin-top:64px">
                          <div class="acontainer">
                            <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTcMmREXrI8ss0rmMnZWKI7L5lL23vbnb3YGQ&usqp=CAU" height="200" width="200" class="abtusimgcul" />
                            <p style="font-size:40px">Culture</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                          <div class="acontainer">
                            <img src="https://artsnorthernrivers.com.au/wp-content/uploads/KateHolmes_ArtsNR_ArtOnBundjalungMkt_2019_49.jpg" class="abtusimgpas" height="200" width="200" />
                            <p style="font-size:40px">Passion</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                          <div class="acontainer">
                            <img src="https://artsnorthernrivers.com.au/wp-content/uploads/2021/02/ANR_Weaving-Book-_Web-Header.jpg" height="200" width="200" class="abtusimgsup" />
                            <p style="font-size:40px">Support</p>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.</p>
                          </div>
                        </div>