I am using Compare-Object
to compare 2 bidimensional arrays issued by Get-ChildItem
.
I am asking it to compare using the Name
property using -Property 'Name'
This returns a table with the Name
property and SideIndicator
.
The problem is that I would like to know not only the name of the file, but also its complete path; if I ask Compare-Object ... -Property @{ Expression = {"$($_.Directory)\$($_.Name)"} }
, it compares using the path, which is not what I want either (this tool I'm coding will be to sync up 2 folders, so obviously the paths are different, hence getting all the files scored as different).
Please let me know if you need more details !
If you use Compare-Object
with -Property
in order to compare the two input collections by one or more specific property values only, by default the output objects contain those properties only, alongside the .SideIndicator
property that signals which of the two input collections a given difference came from:
# Sample input collections.
$c1 = @(Get-Date) # a single-element array containing a [datetime] instance
$c2 = @() # empty array
# Due to -Property Day, the output object contains only the .Day property value,
# not the whole date.
# -> [pscustomobject] @{ Day = 21; SideIndicator = '<=' }
Compare-Object -Property Day -ReferenceObject $c1 -DifferenceObject $c2
-Property
- resulting in whole-object comparisons (which with complex objects are typically meaningless) - do you get the whole input objects by default, namely in the .InputObject
property of the [pscustomobject]
instances that are emitted.To instead pass the whole original objects through, use the -PassThru
switch.
That is, instead of emitting [pscustomobject]
instances with a .SideIndicator
property, -PassThru
causes the input objects themselves to be emitted.
However, so that you can still determine which side (input collection) a given object came from, the original objects are decorated with a .SideIndicator
property, using PowerShell's ETS (Extended Type System).
$c1 = @(Get-Date)
$c2 = @()
# Due to adding -PassThru, the output is now the whole date.
$differingDates =
Compare-Object -Property Day -PassThru $c1 $c2
# The original [datetime] instance was returned, decorated with a
# .SideIndicator property.
$differingDates.SideIndicator # -> '<='
Caveats:
If you combine -PassThru
with -IncludeEqual
, it is invariably only the LHS (-ReferenceObject
) object that is passed through for objects that compare as equal (i.e. whose .SideIndicator
property contains ==
); that is, if two objects compare equal with respect to the given properties but differ with respect to others, you'll only have access to those other properties from the LHS objects.
# Sample input collections.
$c1 = @(Get-Date -Year 2023)
$c2 = @(Get-Date -Year 2022)
# Compare by *day*, with respect to which the two dates are equal.
# !! Invariably outputs the *2023* date.
Compare-Object -Property Day -PassThru -IncludeEqual $c1 $c2
The ETS .SideIndicator
property is usually invisible, such as in the output from the example above, but may situationally unexpectedly surface, such as when you pass a decorated object to ConvertTo-Json
(pipe to Select-Object -Property * -ExcludeProperty SideIndicator
to avoid this).[1]
# Sample type with only one property, .Bar
class Foo { [int] $Bar = 42 }
# Sample input collections.
$c1 = @([Foo]::new())
$c2 = @()
# !! JSON includes .SideIndicator.
# !! -> {"Bar":42,"SideIndicator":"<="}
Compare-Object -PassThru $c1 $c2 |
ConvertTo-Json -Compress
[1] In PowerShell (Core) 7.2+, ConvertTo-Json
now automatically and invariably ignores ETS properties of [string]
and [datetime]
instances, specifically. See GitHub issue #5797 for the original discussion that led to this change.