Tibor
Tibor

Reputation: 23

Find string in array Powershell V2.0

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

Answers (2)

Ansgar Wiechers
Ansgar Wiechers

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

Tomalak
Tomalak

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

Related Questions