Search code examples
csvpowershellexport-to-csvcompareobject

System Object Error when exporting results to CSV


I am trying to export my results from a compare-object into a csv but I get an error When I export it. It looks ok when I just call it in excel. My guess is whenever there is a output of more than one value the error is placed instead of the value.

Here are my csvs past.csv

VKEY
V-12345
V-23456
V-1111

current.csv

VKEY
V-12345
V-6789
V-23456
V-256

My new csv should say

Past, Current
V-6789,V-1111
V-256

What I am getting now is

Past, Current
System.Object[],@{vkey=V-1111}

.

$Past = Import-CSV "past.csv"
$Current = Import-CSV "Current.csv"

$pastchange = Compare-Object $Past $Current -Property vkey | Where-Object {$_.SideIndicator -eq '=>'} | Select-Object VKEY
$currentchange = Compare-Object $Past $Current -Property vkey | Where-Object {$_.SideIndicator -eq '<='} | Select-Object VKEY

$obj = New-Object PSObject
$obj | Add-Member NoteProperty Past $pastchange
$obj | Add-Member NoteProperty Current $currentchange
$obj | Export-Csv "ChangeResults.csv" -NoTypeInformation

Solution

  • That System.Object[] displayed in $obj.Past column is simply an array of custom objects similar to @{vkey=V-1111} in $obj.Past column. Proof:

    PS D:\PShell> $obj
    $obj.Past.Gettype() | Format-Table
    $obj.Current.Gettype()
    "---"
    $obj.Past | ForEach-Object { $_.Gettype() }
    
    Past                                       Current                                  
    ----                                       -------                                  
    {@{vkey=V-6789}, @{vkey=V-256}}            @{vkey=V-1111}                           
    
    IsPublic IsSerial Name                                     BaseType                 
    -------- -------- ----                                     --------                 
    True     True     Object[]                                 System.Array             
    
    IsPublic IsSerial Name                                     BaseType                 
    -------- -------- ----                                     --------                 
    True     False    PSCustomObject                           System.Object            
    ---
    True     False    PSCustomObject                           System.Object            
    True     False    PSCustomObject                           System.Object            
    

    My solution makes use of ArrayList Class (.NET Framework):

    $csvOutFile = "d:\test\ChangeResults.csv"    # change to fit your circumstances
    $PastInFile = "d:\test\past.csv"
    $CurrInFile = "d:\test\curr.csv"
    
    $Past = Import-CSV $PastInFile
    $Curr = Import-CSV $CurrInFile
    
    # compare CSV files and convert results to arrays
    $PastCh=@(,                                     <# always return an array           #>
                $( Compare-Object $Past $Curr -Property vkey | 
                     Where-Object { $_.SideIndicator -eq '=>' } ) |
                 ForEach-Object { $_ | Select-Object -ExpandProperty vkey }
              )
    $CurrCh=@(,                                     <# even if no SideIndicator matches #>
                $( Compare-Object $Past $Curr -Property vkey | 
                     Where-Object { $_.SideIndicator -eq '<=' } ) |
                 ForEach-Object { $_ | Select-Object -ExpandProperty vkey }
              )
    
    [System.Collections.ArrayList]$csvout = New-Object System.Collections.ArrayList($null)
    $auxHash = @{}                                       # an auxiliary hash object
    
    $max = ($CurrCh.Count, $PastCh.Count | Measure-Object -Maximum).Maximum
    for ($i=0; $i -lt $max; $i++) {
        Try { $auxHash.Past = $PastCh.GetValue($i) } Catch { $auxHash.Past = '' }
        Try { $auxHash.Curr = $CurrCh.GetValue($i) } Catch { $auxHash.Curr = '' }
        $csvout.Add((New-Object PSObject -Property $auxHash)) > $null
    }
    $csvout | Format-Table -AutoSize      #  show content: 'variable $csvout'
    
    $csvout | Export-Csv $csvOutFile -NoTypeInformation 
    Get-Content $csvOutFile               #  show content: "output file $csvOutFile"
    

    Output:

    PS D:\PShell> D:\PShell\SO\37753277.ps1
    
    Past   Curr  
    ----   ----  
    V-6789 V-1111
    V-256        
    
    
    "Past","Curr"
    "V-6789","V-1111"
    "V-256",""
    
    PS D:\PShell> 
    

    Here is an alternative for TryCatch blocks:

        <# another approach instead of `Try..Catch`:
        if ($i -lt $PastCh.Count) { $auxHash.Past = $PastCh.GetValue($i)
                           } else { $auxHash.Past = '' }
        if ($i -lt $CurrCh.Count) { $auxHash.Curr = $CurrCh.GetValue($i)
                           } else { $auxHash.Curr = '' }
        #>