2

Trying to use an Object Map to replace a month name with the month number. The use case, is that I'm trying rename files that include names of the month.

$monthNameToNumberMap = @{
  January = '01'
  February = '02'
  March = '03'
  April = '04'
  May = '05'
  June = '06'
  July = '07'
  August = '08'
  September = '09'
  October = '10'
  November = '11'
  December = '12'
}

Write-Host "Desired : 02 20 => ";
"February 20" -Replace '([A-z]{3,9}) (\d{2})', "$monthNameToNumberMap[`$1] `$2"

Write-Host "Desired : 05 03 => ";
"May 03" -Replace '([A-z]{3,9}) (\d{2})', "$monthNameToNumberMap[`$1] `$2"

# receiving : @{January=01; February=02; March=03; April=04; May=05; June=06; July=07; August=08; September=09; October=10; November=11; December=12}[February] 20

I'm failing to access the Object Map's value using the regex match group.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Inkh Su Tesou
  • 372
  • 2
  • 8

3 Answers3

3

You are trying to evaluate the backreference value inside a string replacement pattern, which is impossible. You need to define a callback and pass it to the [regex]::Replace method:

$regex = [regex]'\b([A-Za-z]{3,}) (\d{2})\b'
$callback = {  param($m) $monthNameToNumberMap[$m.Groups[1].Value] + " " + $m.Groups[2].Value }
$regex.Replace("February 20", $callback)

Note that [A-z] matches more than just letters, hence, it is advisable to use [a-zA-Z].

I also added word boundaries to avoid matching strings like AAAAAAAAAA 2222222222222222.

enter image description here

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
1

here's a somewhat different way to do the job. [grin] it uses the built in .MonthNames list to convert the month name to a number.

if you need this to be culture-invariant, you can use [System.Globalization.DateTimeFormatInfo]::InvariantInfo.MonthNames instead of the Get-Culture list.

$DateStringList = @(
    'February 20'
    'January 3'
    'August 14'
    'June 19'
    )
$MonthNameList = (Get-Culture).DateTimeFormat.MonthNames

foreach ($DSL_Item in $DateStringList)
    {
    $MonthName, [int]$DayNumber = $DSL_Item.Split(' ')
    $MonthNumber = $MonthNameList.IndexOf($MonthName) + 1
    'Source date  = {0}' -f $DSL_Item
    '{0} is month # {1}' -f $MonthName, $MonthNumber
    'Numeric date = {0:D2}/{1:D2}' -f $MonthNumber, $DayNumber
    '=' * 20
    }

output ...

Source date  = February 20
February is month # 2
Numeric date = 02/20
====================
Source date  = January 3
January is month # 1
Numeric date = 01/03
====================
Source date  = August 14
August is month # 8
Numeric date = 08/14
====================
Source date  = June 19
June is month # 6
Numeric date = 06/19
====================
Lee_Dailey
  • 7,292
  • 2
  • 22
  • 26
1

Wiktor Stribiżew's helpful answer works in all versions of PowerShell.

In PowerShell Core (v6.1 and above) you now can solve the problem using -replace, namely by passing a script block as the replacement operand:

"February 20" -replace '\b([A-z]{3,9}) (\d{2})\b', { 
  $monthNameToNumberMap[$_.Groups[1].Value] + ' ' + $_.Groups[2].Value
}

$_ inside the script block is the match at hand, as a System.Text.RegularExpressions.Match instance.
The script block's output is used as the replacement string.

mklement0
  • 382,024
  • 64
  • 607
  • 775