class Person {
[String]$Name
[String]$LastName
[int]$Age
[Person[]]$Parents
Person($Name, $LastName, $Age) {
$this.Name = $Name
$this.LastName = $LastName
$this.Age = $Age
}
}
$psmith = [Person]::new('Paul', 'Smith', 46)
$msmith = [Person]::new('Mary', 'Smith', 35)
$nsmith = [Person]::new('Nigel', 'Smith', 11)
$nsmith.Parents = $psmith, $msmith
$pdoe = [Person]::new('Jon', 'Doe', 46)
$ldoe = [Person]::new('Mary', 'Doe', 51)
$adoe = [Person]::new('Aidan', 'Doe', 23)
$adoe.Parents = $pdoe, $ldoe
Compare-Object $nsmith $adoe
Compare-Object $nsmith $adoe -Property Parents
Compare-Object $nsmith $adoe -Property Parents -IncludeEqual
$nsmith.Parents.ToString()
$adoe.Parents.ToString()
Compare-Object $nsmith $adoe -Property {$_.Parents.Name}
Compare-Object $nsmith $adoe -Property { $_.Parents.Age }, { $_.Parents.Name }
Compare-Object $nsmith $adoe -Property { $_.Parents[1].Age }, { $_.Parents[1].Name }
$customObj = [PSCustomObject][ordered]@{Prop='value'}
$customObj.GetType()
$customObjArray = ,$customObj, $customObj
'---------'
$customObjArray -is [Array]
'---------'
$adoe.GetType()
function Compare-Stuff($refObject,$diffObject,$props){
foreach ($prop in ($props)) {
$ref = $refObject.$prop
$diff = $diffObject.$prop
if (!$diff){ $diff = 'null'}
if (!$ref){ $ref = 'null' }
$out = Compare-Object $ref $diff | select @{n='Property';e={$prop}}, InputObject, SideIndicator
$out | Group-Object Property,{$_.SideIndicator -eq '=='} | foreach {
if ($_.Group[0].SideIndicator -eq '==') {
[PSCustomObject][Ordered]@{
Property = $_.Group.Property
ReferenceValue = $_.Group.InputObject
DifferenceValue = $_.Group.InputObject
}
}
else {
[PSCustomObject][Ordered]@{
Property = $_.Group[0].Property
ReferenceValue = ($_.Group.where{ $_.SideIndicator -eq '<=' }).InputObject
DifferenceValue = ($_.Group.where{ $_.SideIndicator -eq '=>' }).InputObject
}
}
}
}
}
$builtinType = 'test'.GetType()
$personType = $adoe.GetType()
$excludeProps = 'DeclaredFields','Assembly','CustomAttributes','DeclaredConstructors','DeclaredMembers','DeclaredMethods','AssemblyQualifiedName','DeclaredProperties','Attributes','GUID','ImplementedInterfaces'
$props = ($builtinType | gm -MemberType Property).Name.where{$_ -notin $excludeProps}
Compare-Stuff $builtinType $personType $props
$customType = ([pscustomobject][ordered]@{prop='value'}).GetType()
Compare-Stuff $customType $personType $props
function Compare-Stuff($ReferenceObject, $DifferenceObject, $MaxDepth = -1, $__Property, $__Depth = 0, [switch]$IncludeEqual, [switch]$ExcludeDifferent, [switch]$PassThru, [switch]$Compact) {
if ($MaxDepth -eq -1 -or $__Depth -le $MaxDepth) {
#check for arrays of PSCustomObjects or arrays of custom class and iterate over those
if (($ReferenceObject -is [array]) -and ($ReferenceObject[0] -is [PSCustomObject] -or $null -eq $ReferenceObject[0].GetType().Namespace)) {
$__Depth++
for ($i = 0; $i -lt $ReferenceObject.Count; $i++) {
#recurse carrying the current Property name + index and Depth values forward
Compare-Stuff $ReferenceObject[$i] $DifferenceObject[$i] -__Property ($__Property + "[$i]") -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -PassThru:$PassThru -Compact:$Compact
}
}
#check for custom classes or PSCutomObjects and iterate over their properties.
elseif ($ReferenceObject -is [PSCustomObject] -or $null -eq $ReferenceObject.GetType().Namespace) {
$__Depth++
foreach ($prop in $ReferenceObject.PSObject.properties.name) {
#build up the property name hiarachry
$newProp = $prop
if ($__Property) {
$newProp = $__Property + '.' + $prop
}
# handle ref. or diff. objects equal null
$refValue = $ReferenceObject.$prop
$diffValue = $DifferenceObject.$prop
if ($Null -eq $refValue) {
$refValue = ''
}
if ($null -eq $diffValue) {
$diffValue = ''
}
#recurse carrying the current Property and Depth values forward
Compare-Stuff $refValue $diffValue -__Property $newProp -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -PassThru:$PassThru -Compact:$Compact
}
}
else {
#if we reach here we are dealing with a scalar or array of scalars that the built-in cmdlet can already deal with
$output = Compare-Object $ReferenceObject $DifferenceObject -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -PassThru:$PassThru |
Select-Object @{n = 'Property'; e = { $__Property } }, @{n = 'Value'; e = { $_.InputObject } }, SideIndicator
if ($Compact) {
$output | Group-Object Property,{$_.SideIndicator -eq '=='} | ForEach-Object {
if ($_.Group[0].SideIndicator -eq '==') {
[PSCustomObject][Ordered]@{
Property = $_.Group.Property
ReferenceValue = $_.Group.Value
DifferenceValue = $_.Group.Value
}
}
else {
[PSCustomObject][Ordered]@{
Property = $_.Group[0].Property
ReferenceValue = ($_.Group.where{ $_.SideIndicator -eq '<=' }).Value
DifferenceValue = ($_.Group.where{ $_.SideIndicator -eq '=>' }).Value
}
}
}
}
else {
$output
}
}
}
}
Compare-Stuff $nsmith $adoe -Compact -IncludeEqual
$peter = [PSCustomObject][ordered]@{Name='Peter'; Colors=('red','black')}
$lisa = [PSCustomObject][ordered]@{Name='Lisa'; Colors=('red','black','green','pink')}
Compare-Stuff $peter $lisa -IncludeEqual -Compact
$Metadata = New-Object System.Management.Automation.CommandMetaData (Get-Command Compare-Object)
$proxyCmd = [System.Management.Automation.ProxyCommand]::Create($Metadata) | Set-Clipboard
function Compare-Object{
[CmdletBinding(HelpUri='https://go.microsoft.com/fwlink/?LinkID=113286', RemotingCapability='None', DefaultParameterSetName='Compact')]
param(
[Parameter(Mandatory=$true, Position=0)]
[AllowEmptyCollection()]
${ReferenceObject},
[Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true)]
[AllowEmptyCollection()]
${DifferenceObject},
[ValidateRange(0, 2147483647)]
[int]
${SyncWindow},
[System.Object[]]
${Property},
[ValidateRange(-1, 20)]
[int]
${MaxDepth} = -1,
[int]
$__Depth = 0,
[string]
$__Property,
[switch]
${ExcludeDifferent},
[switch]
${IncludeEqual},
[Parameter(ParameterSetName='PassThru')]
[switch]
${PassThru},
[string]
${Culture},
[switch]
${CaseSensitive},
[Parameter(ParameterSetName='Compact')]
[switch]
$Compact
)
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Compare-Object', [System.Management.Automation.CommandTypes]::Cmdlet)
###########################################################
if ($MaxDepth -eq -1 -or $__Depth -le $MaxDepth) {
#check for array of objects or custom class
if (($ReferenceObject -is [array]) -and ($ReferenceObject[0] -is [PSCustomObject] -or $null -eq $ReferenceObject[0].GetType().Namespace)) {
$__Depth++
for ($i = 0; $i -lt $ReferenceObject.Count; $i++) {
if ($PassThru){
Compare-Object $ReferenceObject[$i] $DifferenceObject[$i] -__Property ($__Property + "[$i]") -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -PassThru:$PassThru
}
else{
Compare-Object $ReferenceObject[$i] $DifferenceObject[$i] -__Property ($__Property + "[$i]") -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -Compact:$Compact
}
}
}
elseif ($ReferenceObject -is [PSCustomObject] -or $null -eq $ReferenceObject.GetType().Namespace) {
$__Depth++
foreach ($prop in $ReferenceObject.PSObject.properties.name) {
$newProp = $prop
if ($__Property) {
$newProp = $__Property + '.' + $prop
}
#recurse
# handle ref. or diff. objects equal null
$refValue = $ReferenceObject.$prop
$diffValue = $DifferenceObject.$prop
if ($Null -eq $refValue) {
$refValue = ''
}
if ($null -eq $diffValue) {
$diffValue = ''
}
if ($PassThru){
Compare-Object $refValue $diffValue -__Property $newProp -__Depth $__Depth -IncludeEqual:$IncludeEqual: -ExcludeDifferent$ExcludeDifferent -PassThru:$PassThru
}
elseif ($Compact){
Compare-Object $refValue $diffValue -__Property $newProp -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent -Compact:$Compact
}
else{
Compare-Object $refValue $diffValue -__Property $newProp -__Depth $__Depth -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent
}
}
}
else {
if($PSBoundParameters['__Depth']){
$null = $PSBoundParameters.Remove('__Depth')
}
if($PSBoundParameters['MaxDepth']){
$null = $PSBoundParameters.Remove('MaxDepth')
}
if($PSBoundParameters['__Property']){
$prop = $PSBoundParameters['__Property']
$null = $PSBoundParameters.Remove('__Property')
}
if($PSBoundParameters['Compact']){
$compact = $PSBoundParameters['Compact']
$null = $PSBoundParameters.Remove('Compact')
}
$scriptCmd = { & $wrappedCmd @PSBoundParameters }
if ($prop) {
$scriptCmd = {
& $wrappedCmd @PSBoundParameters |
Select-Object @{n = 'Property'; e = { $prop } }, @{n = 'Value'; e = { $_.InputObject } }, SideIndicator
}
if ($compact) {
$scriptCmd = {
& $wrappedCmd @PSBoundParameters |
Select-Object @{n = 'Property'; e = { $prop } }, @{n = 'Value'; e = { $_.InputObject } }, SideIndicator |
Group-Object Property,{$_.SideIndicator -eq '=='} | ForEach-Object {
if ($_.Group[0].SideIndicator -eq '==') {
[PSCustomObject][Ordered]@{
Property = $_.Group.Property
ReferenceValue = $_.Group.Value
DifferenceValue = $_.Group.Value
}
}
else {
[PSCustomObject][Ordered]@{
Property = $_.Group[0].Property
ReferenceValue = ($_.Group.where{ $_.SideIndicator -eq '<=' }).Value
DifferenceValue = ($_.Group.where{ $_.SideIndicator -eq '=>' }).Value
}
}
}
}
}
}
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
}
#############################################################
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
}
}
end
{
try {
$steppablePipeline.End()
} catch {
}
}
<#
.ForwardHelpTargetName Microsoft.PowerShell.Utility\Compare-Object
.ForwardHelpCategory Cmdlet
#>
}
Compare-Object $nsmith $adoe -Compact -IncludeEqual
Compare-Object $nsmith $adoe -IncludeEqual
WordPress conversion from compareObject.ipynb by nb2wp v0.3.1

Tree Stock photos by Vecteezy


Just stumbled upon this. Awesome. Thank you. Tip for anyone else who needs to sort the objects first. Use $object | Sort-Object -Property YourDesiredProperty before passing into the compare-object.
LikeLike
nice one! are you planning to add that hashtable support too?
LikeLike