PSCustomObject only shows last value written

80 views Asked by At

New Powershell developer here. I am using the following code to compare the contents of 2 arrays and write values to a third array. The third array is updated within the inner of a nested For loop. The first loop looks at each of an array's values, and for each value in the first array loops through the second array until it finds a match. When it does, the if statement is true and that's where the PSCustomObject is updated with values from both the first and second arrays.

The logic seems to work fine in finding matches. The problem I am having is that the PSCustomObject always contains only a single row of final values even though the IF statement has been true three times and the array should contain three rows. It's as if every update of the PSCustomObject is overwriting the previous results. So 2 questions: Is this an appropriate use of PSCustomObject, and if so, what am I doing wrong?

for ($i = 0; $i -lt $array.length; $i++) {
    # Output the current item
    $cfID = $array[$i].substring(65, 16)
    for ($j = 0; $j -lt $slotnames.length; $j++) {
        if ($slotnames[$j].deviceID.substring(22, 16) -eq $cfID) {
            $result = [pscustomobject]@{
                result1 = $SlotNames[$j].deviceID
                result2 = $SlotNames[$j].Location
                result3 = $array[$i].substring(1, 19)
            }
            break
        } 
        else {
            write-host 'No match!'
        } 
    }
}
2

There are 2 answers

1
Theo On BEST ANSWER

Simply let PowerShell create the resulting array of objects for you as Matthias and Santiago already commented:

# capture the output objects in variable $result
$result = for ($i = 0; $i -lt $array.length; $i++) {
    $cfID = $array[$i].substring(65, 16)
    for ($j = 0; $j -lt $slotnames.length; $j++) {
        if ($slotnames[$j].deviceID.substring(22, 16) -eq $cfID) {
            # Output a new object so it gets collected in variable $result
            [pscustomobject]@{
                result1 = $SlotNames[$j].deviceID
                result2 = $SlotNames[$j].Location
                result3 = $array[$i].substring(1, 19)
            }
        } 
        else {
            Write-Host 'No match!'
        } 
    }
}

No need to create an array (or ArrayList) before the main loop

5
sirtao On

PROBLEM: You are not ADDING the results to the $result variable: you are ASSIGNING them from zero each time.

SOLUTION(updated):
add $result=[System.Collections.ArrayList]::new() before the Loops then

replace

$result = [pscustomobject]@{
    result1 = $SlotNames[$j].deviceID
    result2 = $SlotNames[$j].Location
    result3 = $array[$i].substring(1, 19)
}    

with

$result.add([pscustomobject]@{
    result1 = $SlotNames[$j].deviceID
    result2 = $SlotNames[$j].Location
    result3 = $array[$i].substring(1, 19)
} )

(or $result.insert($result.count,[pscustomobject]@{...}) if you don't want the printing of the index each addition)

ignore the strike-out part, it's wrong The easiest way to fix this is simply changing
$result = [pscustomobject]@{...}
to
$result += [pscustomobject]@{...}

BUT!

A few suggestions:

  1. Do not use for() when cycling through a collection(arrays, ArrayLists, hashtables, etc), use either $Collection | ForEach-Object or $Collection.foreach()

  2. Use a ArrayList when you are planning to add or remove elements: regular Arrays get destroyed and recreated automagically whenever elements are changed so they are inefficient.
    You can declare them simply with $result=[System.Collections.ArrayList]::new()
    This way you can add elements just with $result.add($ObjectToAdd)
    You can still access the single elements with the usual $result[$index]

    In general, it's better to limit the use of Arrays to fixed-sized, small-sized cases.