Skip to content
AgileLogics
5 min read

Comparing and Grouping Data

Prerequisites: Custom objects, Working with CSV

Compare-Object

Compare-Object finds the differences between two sets:

$expected = 'nginx', 'redis', 'postgres', 'cron'
$running  = 'nginx', 'postgres', 'sshd'

Compare-Object -ReferenceObject $expected -DifferenceObject $running

InputObject SideIndicator
----------- -------------
sshd        =>
redis       <=
cron        <=

<= means "only in the reference" (missing), => "only in the difference" (unexpected). Add -IncludeEqual to see matches too.

Compare objects by property with -Property:

$before = @(
  [pscustomobject]@{ Name = 'app.exe';  Version = '1.2' }
  [pscustomobject]@{ Name = 'lib.dll';  Version = '3.0' }
)
$after = @(
  [pscustomobject]@{ Name = 'app.exe';  Version = '1.3' }
  [pscustomobject]@{ Name = 'lib.dll';  Version = '3.0' }
)
Compare-Object $before $after -Property Name, Version |
  Sort-Object Name, SideIndicator |
  ForEach-Object { '{0} {1} {2}' -f $_.SideIndicator, $_.Name, $_.Version }
<= app.exe 1.2
=> app.exe 1.3

Group-Object

$orders = @(
  [pscustomobject]@{ Customer = 'Ada';   Total = 120 }
  [pscustomobject]@{ Customer = 'Linus'; Total = 45 }
  [pscustomobject]@{ Customer = 'Ada';   Total = 80 }
  [pscustomobject]@{ Customer = 'Grace'; Total = 300 }
  [pscustomobject]@{ Customer = 'Linus'; Total = 55 }
)

$orders | Group-Object Customer | ForEach-Object {
  [pscustomobject]@{
    Customer = $_.Name
    Orders   = $_.Count
    Spent    = [int]($_.Group | Measure-Object Total -Sum).Sum
  }
} | Sort-Object Spent -Descending

Customer Orders Spent
-------- ------ -----
Grace         1   300
Ada           2   200
Linus         2   100

Each group has Name, Count, and Group (the objects in it) — combine with Measure-Object for per-group totals, like SQL's GROUP BY.

Fast lookups: -AsHashTable

$users = @(
  [pscustomobject]@{ Id = 1; Name = 'Ada' }
  [pscustomobject]@{ Id = 2; Name = 'Linus' }
)
$byId = $users | Group-Object Id -AsHashTable
$byId[2].Name
Linus

Build a lookup table once instead of searching a list with Where-Object inside a loop — much faster for large data.