'Rename columns in Select-Object
I have an object with spaces in their properties names. I want to Select-Object @{n='NewName';e={$_.'Old Name'}} for every NoteProperty. Since there is a lot of them, I created this function. Running this code will return an array of hash tables but I can't get the old name $col to be replaced with the actual old name. I think it's because it's bound to a new execution context but I can't make it to work.
function Rename-Columns {
# Array of HashTable splat to rename columns in Select-Object (TSQL SELECT [col with space] as colwithspace)
param (
[string[]]$Columns,
[hashtable]$RenamePattern = @{Replace='\s|\:|\.|\+|\|';With=''} # remove unwanted cars
)
$return = @()
foreach ($col in $Columns) {
$newName = $col -replace $RenamePattern['Replace'], $RenamePattern['With']
$return += @{n="$newName";e={$_.'"$col"'}} # <- can't replace $col
}
$return
}
Solution 1:[1]
Your current approach can't work because by the time you pass the {$_."$col"} block to Select-Object, $col no longer resolves to the value it did when you created the scriptblock inside the loop.
In order to bind the current iterator value of $col to the expression block, you need a closure:
function Rename-Columns {
# Array of HashTable splat to rename columns in Select-Object (TSQL SELECT [col with space] as colwithspace)
param (
[string[]]$Columns,
[hashtable]$RenamePattern = @{Replace='\s|\:|\.|\+|\|';With=''} # remove unwanted cars
)
foreach ($col in $Columns) {
# calculate new name
$newName = $col -replace $RenamePattern['Replace'], $RenamePattern['With']
# close over `{$_.$col}` to bind the current value to `$col`
@{n=$newName;e={$_.$col}.GetNewClosure()}
}
}
GetNewClosure() will see that the $col variable exists in the scope where it was called, and so it copies its value and stores it along-side the scriptblock.
As a result, when Select-Object executes the property expression (at a later time), the scriptblock "remembers" the original value and $col resolves correctly/as expected.
With sample data:
$data = [pscustomobject]@{
'Old Name' = 123
'Schema Name' = 456
}
$originalColumnNames = $data |Get-Member -MemberType Properties |ForEach-Object -MemberName Name
$data |Select -Property @(Rename-Columns $originalColumnNames)
It yields the expected result (spaces replaced in all names):
OldName SchemaName
------- ----------
123 456
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
