Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Answering the PowerShell Word to Phone Challenge

Posted on July 7, 2020September 30, 2020

A few weeks ago, the Iron Scripter challenge was to write code to convert a short string into its numeric valuesusing the alphabet as it is laid out on a telephone.  A number of solutions have already been shared in the comments on the original post. There are certainly a number of ways to meet the challenge. Here's what I worked out.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

Convert Text to Number

I started with a simple hashtable.

$phone = @{
    2 = 'abc'
    3 = 'def'
    4 = 'ghi'
    5 = 'jkl'
    6 = 'mno'
    7 = 'pqrs'
    8 = 'tuv'
    9 = 'wxyz'
}

The keys, coincidentally named, correspond to the telephone keys on my phone. To convert 'help' means finding the value and its key.

phone1

The Name property shows me the key.  I can repeat this process for the other letters, join the keys and get my result: 4357. Here's the function I'm using to make this easier and meet some of the challenge's other requirements.

Function Convert-TextToNumber {
[cmdletbinding()]
[Outputtype("int32")]

    Param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [ValidatePattern("^[A-Za-z]{1,5}$")]
        [string]$Plaintext,
        [Parameter(HelpMessage = "A custom dictionary file which is updated everytime a word is converted")]   
        [string]$Dictionary = "mywords.txt"
    )

    Begin {
        Write-Verbose "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"

        #define a simple hashtable
        $phone = @{
            2 = 'abc'
            3 = 'def'
            4 = 'ghi'
            5 = 'jkl'
            6 = 'mno'
            7 = 'pqrs'
            8 = 'tuv'
            9 = 'wxyz'
        }

        #initialize a dictionary list
        $dict = [System.Collections.Generic.List[string]]::new()
        if (Test-Path -path $Dictionary) {
            Write-Verbose "[BEGIN  ] Loading user dictionary from $Dictionary"            
            (Get-Content -path $Dictionary).foreach({$dict.add($_)})
            $originalCount = $dict.Count
            Write-Verbose "[BEGIN  ] Loaded $originalCount items"
        }
    } #begin
    Process {
        Write-Verbose "[PROCESS] Converting: $Plaintext"
        #add plaintext to dictionary if missing
        if ($dict -notcontains $Plaintext) {
            Write-Verbose "[PROCESS] Adding to user dictionary"
            $dict.Add($plaintext)
        }

        #this is a technically a one-line expression
        #get the matching key for each value and join together
        ($plaintext.ToCharArray()).ForEach({
         $val = $_ 
         ($phone.GetEnumerator().Where({$_.value -match $val}).name)}) -join ""
    } #process

    End {
        #commit dictionary to file if new words were added
        if ($dict.count -gt $originalCount) {
            Write-Verbose "[END    ] Ending: Saving updated dictionary $Dictionary"
            $dict | Out-File -FilePath $Dictionary -Encoding ascii -Force
        }
        Write-Verbose "[END    ] Ending: $($MyInvocation.Mycommand)"
    } #end
}
    

The function accepts pipelined input of words that are no more than 5 characters long AND can only be alphabet characters, either upper or lower case. That's what I'm doing with the ValidatePattern regular expression.

I am also planning ahead and keeping a text file of all converted words. If the plaintext doesn't exist in the file, it gets added. If the number of items in the dictionary list changes, I update the file in the End block. Note that I'm using a generic list object and not an array.

$dict = [System.Collections.Generic.List[string]]::new()

This type of object performs much better than an array. The difference is insignificant in this function, but I am moving more to this model instead of traditional arrays.

The conversion is done in the Process block with this one-line expression.

($plaintext.ToCharArray()).ForEach({
         $val = $_ 
         ($phone.GetEnumerator().Where({$_.value -match $val}).name)}) -join ""

Here's a taste of the function in action.

phone2

Convert Number to Text

Converting back is only a little more complex. I can use the same hashtable. This time, I need to get the value associated with each key. I can then build a regular expression pattern to search through my dictionary file. Let me showyou the Verbose output first to illustrate.phone3

This word file had everyting I had converted. Here's the same thing using a larger word file.phone4

Remember the phrase I converted earlier?phone5

It would take a little work but I could probably figure this out. Certainly, a higher quality word file helps. Which is why I started keeping track of what I converted.

phone6Here's the complete function.

Function Convert-NumberToText {
[cmdletbinding()]
    Param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [ValidateRange(2, 99999)]
        [int]$NumericValue,
        [Parameter(Mandatory,HelpMessage= "Specify the path to the word dictionary file.")]
        [ValidateScript({Test-Path $_})]
        [string]$Dictionary
    )

    Begin {
        Write-Verbose "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"

        $phone = @{
            2 = 'abc'
            3 = 'def'
            4 = 'ghi'
            5 = 'jkl'
            6 = 'mno'
            7 = 'pqrs'
            8 = 'tuv'
            9 = 'wxyz'
        }

        Write-Verbose "[BEGIN  ] Loading a word dictionary from $dictionary"
        $words = Get-Content -path $Dictionary

        Write-Verbose "[BEGIN  ] ...$($words.count) available words."

        #define a regex to divide the numeric value
        [regex]$rx = "\d{1}"

    } #begin
    Process {
        Write-Verbose "[PROCESS] Converting: $NumericValue"
        $Values = ($rx.Matches($NumericValue).value).ForEach({ $val = $_ ; ($phone.GetEnumerator() | Where-Object {$_.name -match $val}).value})

        Write-Verbose "[PROCESS] Converting: Possible values: $($Values -join ',')"
        #build a regex pattern with a word boundary
        $pattern = "\b"
        foreach ($value in $values ) {
            $pattern += "[$value]{1}"
        }
        $patternb += "\b"

        Write-Verbose "[PROCESS] Using pattern: $pattern"
        #output will be lower case
        [System.Text.RegularExpressions.Regex]::Matches($words, $pattern, "IgnoreCase") |
        Group-Object -Property Value |
        Select-Object -ExpandProperty Name |
        Sort-Object | 
        Foreach-Object {$_.tolower()}

    } #process

    End {
        Write-Verbose "[END    ] Ending: $($MyInvocation.Mycommand)"
    } #end
}
    

And if you want to try my larger word file, download it here.

Learn More

There were a number of terrific learning opportunities in this challenge. If you find yourself wanting or needing to learn more, I encourage you to check out the PowerShell content on Pluralsight.com. I even have a course on PowerShell and Regular Expressions. And to really take your scripting to the next level, grab a copy of The PowerShell Scripting and Toolmaking book.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d