Alex
Alex

Reputation: 65

How to populate two arrays as result of Compare-Object

I'm trying to do some checks when loading a CSV and inform the user of what extra headers were found and which ones are still missing. $headers_required will be hardcoded, $headers_loaded will be populated by Import-CSV The goal is to populate the two arrays with the differences from the other array, like diff in Unix.

$headers_required=@( `
'Path', `
'GivenName', `
'Name', `
'EmailAddress', `
'sAMAccountName', `
'psw-temp', `
'Path', `
'ChangePasswordAtLogon' `
)

$headers_loaded=@( `
'Path', `
'GivenName', `
'Name', `
'PhoneNumber', `
'PreferredFilm', `
'sAMAccountName', `
'psw-temp', `
'Path', `
'ChangePasswordAtLogon', `
'BankAccount' `
)

Eventually I would like to have

$extra_headers_not_nedeed=@('BankAccount', 'PhoneNumber', 'PreferredFilm')
$missing_headers=@('EmailAddress')

I can filter them in this way

$extra_headers_not_nedeed=Compare-Object ($headers_required|Sort-Object) ($headers_loaded|Sort-Object) | Where SideIndicator -eq '<='
$missing_headers=Compare-Object ($headers_required|Sort-Object) ($headers_loaded|Sort-Object) | Where SideIndicator -eq '=>'

And I do filter them

PS C:\> $missing_headers|Write-host
@{InputObject=EmailAddress; SideIndicator=<=}

PS C:\> $extra_headers_not_needed|Write-host
@{InputObject=BankAccount; SideIndicator==>}
@{InputObject=PhoneNumber; SideIndicator==>}
@{InputObject=PreferedFilm; SideIndicator==>}
  1. how can I get normal arrays as specified above instead of a structured object? I tried $missing_headers['InputObject'] but it returns the whole object instead of 'EmailAddress'

  2. is a there a more compact way (aside symbols) to get to the same result?

TIA

Upvotes: 1

Views: 121

Answers (4)

Santiago Squarzon
Santiago Squarzon

Reputation: 60035

I would use a function that you can re-use and can dynamically assign new reference headers to compare with your Csv.

I wouldn't use Compare-Object personally, I think it takes all the fun away.

function Test-Csv {
param(
    [parameter(mandatory)]
    [string[]]$ReferenceHeaders,
    [parameter(mandatory)]
    [object]$InputObject
)
    
    $csvHeaders = $InputObject[0].PSObject.Properties.Name
    $valid, $missing = $ReferenceHeaders.where({ $_ -in $csvHeaders }, 'Split')
    $invalid = $csvHeaders.where({ $_ -notin $ReferenceHeaders })
    
    $valid.foreach({
        [pscustomobject]@{
            HeaderName = $_
            Status = 'Valid'
        }
    })
    $missing.foreach({
        [pscustomobject]@{
            HeaderName = $_
            Status = 'Missing'
        }
    })
    $invalid.foreach({
        [pscustomobject]@{
            HeaderName = $_
            Status = 'Invalid'
        }
    })
}

Usage

# Define the Reference Headers
$requiredHeaders =@( 
    'Path' 
    'GivenName' 
    'Name' 
    'EmailAddress' 
    'sAMAccountName' 
    'psw-temp' 
    'ChangePasswordAtLogon' 
)

# This example Csv would be the one you import from a file as an example
$exampleCsv = @'
GivenName,sAMAccountName,RandomHeader1,Path,RandomHeader2
z,z,z,z,z
'@ | ConvertFrom-Csv

PS \> Test-Csv -ReferenceHeaders $requiredHeaders -InputObject $exampleCsv

HeaderName            Status 
----------            ------ 
Path                  Valid  
GivenName             Valid  
sAMAccountName        Valid  
Name                  Missing
EmailAddress          Missing
psw-temp              Missing
ChangePasswordAtLogon Missing
RandomHeader1         Invalid
RandomHeader2         Invalid

PS \> $result = Test-Csv -ReferenceHeaders $requiredHeaders -InputObject $exampleCsv

PS \> $result | Where-Object Status -EQ 'Missing'

HeaderName            Status 
----------            ------ 
Name                  Missing
EmailAddress          Missing
psw-temp              Missing
ChangePasswordAtLogon Missing

PS \> $result | Where-Object Status -EQ 'Invalid'

HeaderName    Status 
----------    ------ 
RandomHeader1 Invalid
RandomHeader2 Invalid

Upvotes: 2

RetiredGeek
RetiredGeek

Reputation: 3158

Just another option:

Clear-Host

$Extra_Headers_Not_Needed = New-Object -TypeName 'System.Collections.ArrayList'
$Missing_Headers          = New-Object -TypeName 'System.Collections.ArrayList'
$x = Compare-Object ($headers_required|Sort-Object) ($headers_loaded|Sort-Object)

ForEach ( $header in $x) {
   Switch  (($Header).SideIndicator) {
   '=>' {$Null = $Extra_Headers_Not_Needed.Add($Header.InputObject)  ; Break}
   '<=' {$Null = $Missing_Headers.Add($Header.InputObject)           ; Break}
   Default {"Error: $($Header.InputObject)" }
   }
}
"Not Needed $($Extra_Headers_Not_Needed.Count) :"
$Extra_Headers_Not_Needed
"`nMissing Headers $($Missing_Headers.Count) :"
$Missing_Headers

Output:

Not Needed 3 :
BankAccount
PhoneNumber
PreferredFilm

Missing Headers 1 :
EmailAddress

Upvotes: 2

Alex
Alex

Reputation: 65

Thanks Zdan and Santiago,

eventually I found this way too (from the a[key]='value' concept)

PS C:\> $missing_headers.'InputObject'
BankAccount
PhoneNumber
PreferredFilm

PS C:\> $extra_headers_not_nedeed.'InputObject'
EmailAddress

Because of

PS C:> $extra_headers_not_nedeed.psobject.properties

MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : EmailAddress
TypeNameOfValue : System.String
Name            : InputObject
IsInstance      : True

MemberType      : NoteProperty
IsSettable      : True
IsGettable      : True
Value           : <=
TypeNameOfValue : System.String
Name            : SideIndicator
IsInstance      : True

Upvotes: 0

zdan
zdan

Reputation: 29450

Add the -PassThru argument to the compare-object command:

# ~> $extra_headers_not_nedeed=Compare-Object ($headers_required|Sort-Object) ($headers_loaded|Sort-Object) -passthru| Where SideIndicator -eq '<='
# ~> $extra_headers_not_nedeed
EmailAddress

# ~> $missing_headers=Compare-Object ($headers_required|Sort-Object) ($headers_loaded|Sort-Object) -PassThru | Where SideIndicator -eq '=>'
# ~> $missing_headers
BankAccount
PhoneNumber
PreferredFilm

Upvotes: 2

Related Questions