Reputation: 23
I have trouble with a script which is working in PowerShell V5.0 but doesn't work in V2.0.
I need to compare two columns in two CSV files and edit one value in one CSV if it exists in both files.
This is an example with the problem. $a
is renamed in V5.0 to 1X and in V2.0 it is still 1. I try to use Compare
but that doesn't solve the problem for me. Upgrading to V5.0 on that specific machine is unfortunately not an option. Any advice you could offer would be greatly appreciated.
# create examples
$a = @()
$itema = New-Object PSObject
$itema | Add-Member -Type NoteProperty -Name 'ID' -Value '1'
$a += $itema
$b = @()
$itemb = New-Object PSObject
$itemb | Add-Member -Type NoteProperty -Name 'ID' -Value '1'
$b += $itemb
# problem
foreach ($value in $a) {
$ID = $value.ID
if ($b.ID -contains $ID) {$value.ID = $ID + "X"}
}
Upvotes: 2
Views: 13718
Reputation: 200543
PowerShell v2 doesn't support member enumeration, which is what you're using here:
$b.ID -contains $ID
The feature was introduced with PowerShell v3.
In PowerShell v2 $b.ID
tries to expand a property ID
of the array $b
, which produces an empty result because the array object doesn't have such a property.
In PowerShell v3 and newer $b.ID
tries to expand a property ID
on the elements of the array $b
(if the array object itself doesn't have that property).
To make your code compatible to PowerShell v2 change this:
if ($b.ID -contains $ID) {$value.ID = $ID + "X"}
into this:
if (@($b | Select-Object -Expand ID) -contains $ID) {$value.ID = $ID + "X"}
Addendum:
For performance reasons it would make sense to expand the ID list just once outside the loop and store it in a separate variable
$id_list = @($b | Select-Object -Expand ID)
and then use that variable in the comparison:
if ($id_list -contains $ID) {$value.ID = $ID + "X"}
If you're really pressed for performance I'd recommend creating a hashtable from the IDs
$id_list = @{}
foreach ($e in $b) {
$id_list[$e.ID] = $true
}
and do the comparison like this:
if ($id_list.ContainsKey($ID)) {$value.ID = $ID + "X"}
Hashtable lookups provide better performance than -contains
operations because the former does an index lookup while the latter does a linear search on an array.
Upvotes: 2
Reputation: 338406
This is because Powershell 2.0 does not support the feature that lets you select multiple properties of the same name from the objects in an array.
In later Powershell versions, $b.ID
will give you an array of all ID
values from the objects in $b
. In Powershell 2.0 this does not happen, $.ID
is $null
, because there is no ID
property on the array itself.
You can extract the array like this:
$known_IDs = $b | ForEach-Object { $_.ID }
After that, this works in PowerShell 2.0:
foreach ($item_a in $a) {
if ($known_IDs -contains $item_a.ID) {
$item_a.ID += "X"
}
}
Or, as a pipeline:
$a | Where-Object { $known_IDs -contains $_.ID } | ForEach-Object { $_.ID += "X" }
Upvotes: 1