Two weeks ago, I started a PowerShell contest which required the participants to convert a string of text to title case. I didn’t specifically say title case but I explained that the first letter of each word should be converted to upper case and all remaining letters in each word should be converted to lower case. This was because a search on how to convert to title case with PowerShell gave away a good portion of the answer.
There were a total of 22 entries in the contest and all of them were submitted as a secret gist as stated in the requirements for the contest. A link to each entry can be found in the comments of my “Windows PowerShell TFM Book Contest and Giveaway” blog article. Overall, I would rate all of the submissions as good or better although some did a lot more work than was required to accomplish the task.
I went through each entry, evaluated it, and thoroughly tested each one. Based on the results that I found, I chose Jeff Buenting’s entry as the winner:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | Function Format-StringToTitleCase { <# .Description Capitalizes the first letter of each word in a string of text and converts the remaining letters in each word to lower case. .Parameter Text Sentence or string of text. .Example Pipe text to Format-StringtoTitleCase 'THIS IS,A tesT','ThE wINDOWS PowerShell TFM book CONTEST aNd GiVeAwAy' | Format-StringtoTitleCase .Example Pass an array os strings to Format-StringtoTitleCase Format-StringtoTitleCase -Text 'THIS IS,A tesT','ThE wINDOWS PowerShell TFM book CONTEST aNd GiVeAwAy' .Notes Author: Jeff Buenting Date: September 23, 2015 .Link http://mikefrobbins.com/2015/09/23/windows-powershell-tfm-book-contest-and-giveaway/ #> [CmdletBinding()] Param ( [Parameter(Mandatory=$True, ValueFromPipeLine=$True)] [String[]]$Text ) Process { Foreach ( $T in $Text ) { Write-Verbose "Processing: $T" # ---- Convert text to lowercase and then using the ToTitleCase Method from System.Globalization.Textinfo class (Get-Culture) Write-OutPut (Get-Culture).textinfo.totitlecase( $T.ToLower() ) } } } |
His entry used a function with a Verb-Noun name, an approved verb, singular noun, and a pascal cased name. One thing that was missing from Jeff’s entry was a Requires statement to tell me what version was required by his function, but none of the entries were perfect. His entry included comment based help, although it was missing a synopsis which I typically include. It included both pipeline and parameter input along with making the parameter mandatory. I also like that Jeff went the extra mile and included support for multiple strings of text instead of just one string of text. That added a very small amount of unnecessary complexity but overall his entry was simple, clear, and concise.
There were a number of entries that didn’t use the Get-Culture cmdlet to accomplish the task which created a lot of unnecessary complexity. Some of those entries also added things like an extra space at the end of the string which showed up in the Pester tests that I ran against each entry:
Before I had even thought about using this task for a contest, I had written a simple solution for it. My original solution didn’t of course include comment based help among other things, but it is unique and I was actually surprised that no one else submitted a similar solution:
1 | filter ConvertTo-MrTitleCase {(Get-Culture).TextInfo.ToTitleCase($_.ToLower())} |
I decided to take Jeff’s entry and update it with what I thought it was missing along with a few things that are just personal preferences of mine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #Requires -Version 3.0 function ConvertTo-MrTitleCase { <# .SYNOPSIS Capitalizes the first letter of each word in a string of text and converts the remaining letters in each word to lower case. .DESCRIPTION ConvertTo-MrTitleCase is an advanced function that converts each word in one or more strings of text to title case. .PARAMETER Text One or more sentences or strings of text. .EXAMPLE 'THIS IS,A tesT', 'ThE wINDOWS PowerShell TFM book CONTEST aNd GiVeAwAy' | ConvertTo-MrTitleCase .EXAMPLE ConvertTo-MrTitleCase -Text 'THIS IS,A tesT', 'ThE wINDOWS PowerShell TFM book CONTEST aNd GiVeAwAy' .INPUTS String .OUTPUTS String #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [String[]]$Text ) PROCESS { foreach ($t in $Text) { Write-Verbose -Message "Processing: $t" Write-Output (Get-Culture).TextInfo.ToTitleCase($t.ToLower()) } } } |
Congratulations to Jeff and thanks to everyone who took the time to participate in this contest.
µ
Congratulations Jeff
Thanks for the fun Mike
Out of curiosity, is it necessary to have the additional calling of ToLower() for the string object? I did a bit of testing and found that the ToTitleCase function will set title case just fine (unless I’m missing some rare instances) without having to set all to lowercase first. This also saves execution time as well.
I liked the idea of a filter, and to be honest it only occurred to me after posting my initial gist. I wondered if anyone else would have considered using a filter instead of a function as well and it turns out nobody else did! Is this a lost/forgotten feature in powershell??
@peter
All uppercase words will not covert to title case for the default settings for en-US (and possibly more)
This may not be true for every possible variation of culture setting but we account for it just in case.
PS C:\Users\Jonathan> (Get-Culture).TextInfo.ToTitleCase(“THIS IS A TEST”)
THIS IS A TEST
PS C:\Users\Jonathan> (Get-Culture).TextInfo.ToTitleCase((“THIS IS A TEST”).ToLower())
This Is A Test
ah ha! And there’s the instance I was overlooking… Albeit not as rare an instance, I certainly did not consider it! Thanks Jon.