Combine and condense PowerShell arrays I need to combine 3 sets of arrays that contain PsCustomObject elements, such that the unique elements of each array is only listed once, but has properties that indicate the source.

# create test cases
$a = [PsCustomObject]@{Id='AAA';Name='Dev'}
$b = [PsCustomObject]@{Id='BBB';Name='Dev/Test'}
$c = [PsCustomObject]@{Id='CCC';Name='Dev/Prod'}
$d = [PsCustomObject]@{Id='DDD';Name='Dev/Test/Prod'}
$e = [PsCustomObject]@{Id='EEE';Name='Test'}
$f = [PsCustomObject]@{Id='FFF';Name='Test/Prod'}
$g = [PsCustomObject]@{Id='GGG';Name='Prod'}

# add to arrays
$dev=@()
$dev += $a
$dev += $b
$dev += $c
$dev += $d

$test=@()
$test += $b
$test += $d
$test += $e
$test += $f

$prod=@()
$prod+=$c
$prod+=$d
$prod+=$f
$prod+=$g

# array to contain the results
$result=@()

# process dev list; decorate w/ additional properties
$dev | % { 
    Add-Member -InputObject $_ -NotePropertyName "DEV" -NotePropertyValue "Y" 
    Add-Member -InputObject $_ -NotePropertyName "TEST" -NotePropertyValue $null
    Add-Member -InputObject $_ -NotePropertyName "PROD" -NotePropertyValue $null
    # add to results
    $result += $_
}

# process test list
$test | % {

    $match = $result -match $_.Id

    # if a test element matches an element in the results
    if ($match) {
        # set the column
        $match[0].TEST='Y'
    }
    # if this element is not in the results
    else {
        # additional properties
        Add-Member -InputObject $_ -NotePropertyName "DEV" -NotePropertyValue $null
        Add-Member -InputObject $_ -NotePropertyName "TEST" -NotePropertyValue "Y"
        Add-Member -InputObject $_ -NotePropertyName "PROD" -NotePropertyValue $null
        # add to results
        $result += $_    
    }
}

# process test list
$prod | % {

    $match = $result -match $_.Id

    # if a test element matches an element in the results
    if ($match) {
        # set the column
        $match[0].PROD='Y'
    }
    else {
        # additional properties
        Add-Member -InputObject $_ -NotePropertyName "DEV" -NotePropertyValue $null
        Add-Member -InputObject $_ -NotePropertyName "TEST" -NotePropertyValue $null
        Add-Member -InputObject $_ -NotePropertyName "PROD" -NotePropertyValue 'Y'
        # add to results
        $result += $_    
    }
}

# display the results
$result | format-table -AutoSize


the results of the script:

Id  Name          DEV TEST PROD
--  ----          --- ---- ----
AAA Dev           Y            
BBB Dev/Test      Y   Y        
CCC Dev/Prod      Y        Y   
DDD Dev/Test/Prod Y   Y    Y   
EEE Test              Y        
FFF Test/Prod         Y    Y   
GGG Prod                   Y   


I realize that I could refactor the search logic into a single function, but my concern is the search logic itself.

Is there a more-efficient way to perform this logic?
Assuming your objects have a unique property, such as the Id property in your example, you can expand each array in a single pipeline and use Select -Unique to create a combined array, and add your tracking in the same command as such:

$result = $dev,$test,$prod|%{$_}|Select *,@{l='Dev';e={$_.Id -in $dev.id}},@{l='Prod';e={$_.id -in $prod.id}},@{l='Test';e={$_.id -in $test.id}} -Unique


That cuts the time down to a bit under half what you are seeing. I ran Measure-Command 100 times against your code vs my code (using the same test build in each, the only difference being how $result was generated), and your code on my machine took about 16.9ms on average, and my code took 7.4ms on average.

1..100|%{Measure-Command {# create test cases
$a = [PsCustomObject]@{Id='AAA';Name='Dev'}
$b = [PsCustomObject]@{Id='BBB';Name='Dev/Test'}
$c = [PsCustomObject]@{Id='CCC';Name='Dev/Prod'}
$d = [PsCustomObject]@{Id='DDD';Name='Dev/Test/Prod'}
$e = [PsCustomObject]@{Id='EEE';Name='Test'}
$f = [PsCustomObject]@{Id='FFF';Name='Test/Prod'}
$g = [PsCustomObject]@{Id='GGG';Name='Prod'}

# add to arrays
$dev = $a,$b,$c,$d

$test = $b,$d,$e,$f

$prod = $c,$d,$f,$g

$results = $dev,$test,$prod|%{$_}|Select *,@{l='Dev';e={$_.Id -in $dev.id}},@{l='Prod';e={$_.id -in $prod.id}},@{l='Test';e={$_.id -in $test.id}} -Unique

$Results|FT -Auto
}}|Measure-Object -Average -Property TotalMilliseconds|% Average

7.408363