Search code examples
powershellpowershell-4.0

PowerShell - Convert Property Names from Pascal Case to Upper Case With Underscores


Let's say I have an object like this:

$test = @{
          ThisIsTheFirstColumn = "ValueInFirstColumn"; 
          ThisIsTheSecondColumn = "ValueInSecondColumn"
         }     

and I want to end up with:

$test = @{
          THIS_IS_THE_FIRST_COLUMN = "ValueInFirstColumn"; 
          THIS_IS_THE_SECOND_COLUMN = "ValueInSecondColumn"
         } 

without manually coding the new column names.

This shows me the values I want:

$test.PsObject.Properties | where-object { $_.Name -eq "Keys" } | select -expand value | foreach{ ($_.substring(0,1).toupper() + $_.substring(1) -creplace '[^\p{Ll}\s]', '_$&').Trim("_").ToUpper()} | Out-Host

which results in:

THIS_IS_THE_FIRST_COLUMN
THIS_IS_THE_SECOND_COLUMN

but now I can't seem to figure out how to assign these new values back to the object.


Solution

  • You can modify hashtable $test in place as follows:

    foreach($key in @($test.Keys)) { # !! @(...) is required - see below.
      $value = $test[$key] # save value
      $test.Remove($key)   # remove old entry
      # Recreate the entry with the transformed name.
      $test[($key -creplace '(?<!^)\p{Lu}', '_$&').ToUpper()] = $value
    }
    

    @($test.Keys) creates an array from the existing hashtable keys; @(...) ensures that the key collection is copied to a static array, because using the .Keys property directly in a loop that modifies the same hashtable would break.

    The loop body saves the value for the input key at hand and then removes the entry under its old name.[1]

    The entry is then recreated under its new key name using the desired name transformation:

    $key -creplace '(?<!^)\p{Lu} matches every uppercase letter (\p{Lu}) in a given key, except at the start of the string ((?<!^)), and replaces it with _ followed by that letter (_$&); converting the result to uppercase (.ToUpper()) yields the desired name.


    [1] Removing the old entry before adding the renamed one avoids problems with single-word names such as Simplest, whose transformed name, SIMPLEST, is considered the same name due to the case-insensitivity of hasthables in PowerShell. Thus, assigning a value to entry SIMPLEST while entry Simplest still exists actually targets the existing entry, and the subsequent $test.Remove($key) would then simply remove that entry, without having added a new one.
    Tip of the hat to JosefZ for pointing out the problem.