Két objektum tulajdonságainak összehasonlítása (Compare-ObjectProperty)

Gyakran adódik olyan helyzet, hogy nem egy konkrét tulajdonságra akarunk keresni, hanem két hasonló objektumot akarunk összehasonlítani és feltárni a tulajdonságaik közti különbségeket. Ugyan létezik a Compare-Object cmdlet, de ez a nevével ellentétben nem objektumokat, hanem gyűjteményeket hasonlít össze. Kellene készíteni egy Compare-ObjectProperty függvényt!

A nehézség abban rejlik, hogy hogy itt nem csak háromfajta eset lehetséges az összehasonlítás során (<=, ==, =>), hanem több is, ráadásul ezek nem is teljesen egyértelműek. Természetesen a két objektum azonos nevű tulajdonságai lehetnek egyformák (==) és jelöljük a nyilakkal (<=, =>) azt, ha csak az egyikben vagy csak a másikban van meg az adott tulajdonság. Viszont, ha mindkettőben megvan ugyanaz a tulajdonság, csak más az értékük, akkor azt jelölhetjük ezzel: <>.

Kezdjük tehát összerakni ezt az összehasonlító függvényt!

function Compare-ObjectProperty1 {

param(

    $referenceobject,

    $differenceobject

)

   

    $allprops = @()

    $rp = @()

    $dp = @()

    $rs = "r:"

    $ds = "d:"

 

    if($referenceobject){

        $rp = $referenceobject.psobject.Properties |

                Select-Object -ExpandProperty Name

        $allprops = @($rp)

        $rs += $referenceobject.tostring()

    }

 

    if($differenceobject){

        $dp = $differenceobject.psobject.Properties |

                Select-Object -ExpandProperty Name

        foreach($p in $dp){

            if($allprops -notcontains $p){

                $allprops += $p

            }

        }

        $ds += $differenceobject.tostring()

    }

 

    foreach($p in ($allprops | Sort-Object)){

        $ra = $referenceobject.$p

        $da = $differenceobject.$p

        $rtype = if($null -eq $ra){"NULL"}else{$ra.gettype().fullname}

        $dtype = if($null -eq $da){"NULL"}else{$da.gettype().fullname}

 

        if($null -eq $ra -and $null -ne $da){

            $equal = "=>"

        }

        elseif($null -ne $ra -and $null -eq $da){

            $equal = "<="

        }

        elseif($rtype -ne $dtype){

            $equal = "<>"

        }

        elseif($ra -is [collections.ilist]){

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da){

                $equal = "<>"

            } 

            else{

                $equal = "=="

            }

        }

        elseif($ra -is [collections.idictionary]){

            $ra = @($ra.getenumerator())

            $da = @($da.getenumerator())

 

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da -Property Key, Value){

                $equal = "<>"

            }

            else{

                $equal = "=="

            }

        }

        else{

            $equal = if($ra -eq $da){"=="}else{"<>"}

        }

 

        [pscustomobject]@{

            Property = $p

            Equal    = $equal

            $rs      = $ra

            $ds      = $da

        }

    }

}

Két paraméterünk van egyelőre, a $referenceobject és a $differenceobject. Az $allprops-ban gyűjtöm az összes tulajdonságnevet, az $rp-ben a referencia tulajdonságneveit, a $dp-ben a differenciáét. Miután a végeredményből egy táblázatot szeretnék, az egyik oszlopnévben a differencia objektumra akarok hivatkozni, ennek az oszlopnak a neve „r:”-al kezdődik, hasonlóan a másik „d:”-al, ezeket az $rs és $ds változókba rakom, amiket majd megtoldom az adott objektumok sztringesített változatával.

A tulajdonságnevek gyűjtögetése utáni foreach ciklusban végig megyek a sorba rakott összes tulajdonságon és kitalálom, hogy vajon az adott tulajdonságok értékei a két objektumban vajon milyen viszonyban vannak egymással a fenti négy lehetőség közül. Az egyformaság egyik döntő kérdése, hogy vajon egyforma típusúak-e a tulajdonság értékei? Mert ha nem, akkor nem is nagyon kell tovább vizsgálgatni őket, hanem az eredmény „<>” lesz. Így az egyes típusokat az $rtype és $dtype változókba beteszem, itt külön eset, ha valamelyik $null, hiszen a semminek nincs tulajdonsága, ezért erre én „NULL”-t teszek oda be.

Előtte még attól függően, hogy a referencia vagy differencia objektum üres-e generálom a $equal változóba a „=>” vagy „<=” nyilakat. A típusok különbözőségének vizsgálata után már a következő elseif ágban már elég a referencia objektum típusára kitételeket tenni, hiszen a másik objektum is, ha már idáig eljutottunk, akkor ugyanolyan. Ha tehát egyik objektum tömbszerű ([collections.ilist]), akkor a Compare‑Object-el határozhatom meg az egyformaságukat. Ha a Compare-Object-nek van kimenete, akkor az azt jelenti, hogy nem egyforma a kettő, ha nincs kimenet, akkor egyforma.

Hasonlóan ehhez, a következő elseif-ben, ha szótárszerűek ([collections.idictionary]) az objektumok, akkor előbb tömbbé alakítom a szótárakat a GetEnumerator() metódussal és utána már szintén a Compare-Object-el hasonlítom őket össze.

A legvégén már a sima -eq operátorral hasonlítom össze őket. Kimenetként egyedi objektumokat bocsátok ki, melynek első tulajdonsága maga a vizsgált tulajdonság neve ($p), majd az összehasonlítás eredménye ($equal), majd a referencia objektum, végül a differencia objektum adott tulajdonságának értéke.

Nézzük meg működés közben is a függvényt! Ehhez kreálok két egyéni objektumot, amely rendelkezik mindenféle típusú tulajdonsággal:

$o1 = [pscustomobject]@{

    értékegyforma = 1

    értékkülönböző = "bla"

    hashegyforma = @{

        key1 = "bla"

        Key2 = "BlaBla"

    }

    hashkülönböző = @{

        keyegy = 1

        keykettő = 2

    }

    tömbegyforma = 1,2,3

    tömbkülönböző = 'a', 'b'

    kétfajtasemmi = ""

    semmi = $null

    csakitt = "szöveg"

}

 

$o2 = [pscustomobject]@{

    értékegyforma = 1

    értékkülönböző = "kakukk"

    hashegyforma = @{

        key1 = "bla"

        Key2 = "BlaBla"

    }

    hashkülönböző = @{

        keyegy = 5

        keyhárom = 3

    }

    tömbegyforma = 1,2,3

    tömbkülönböző = 'c', 'b'

    kétfajtasemmi = @()

    semmi = $null

}

Van itt minden, mint a búcsúban! Egyforma és különböző érték típusú tulajdonság, egyforma és különböző hashtábla, egyforma és különböző tömb, különböző és egyforma „semmi” és végül egy olyan tulajdonság, ami csak a referencia objektumban van. Ezeket összehasonlítva ezt kapjuk:

PS C:\> Compare-ObjectProperty1 -referenceobject $o1 -differenceobject $o2

 

Property       Equal r:                 d:

--------       ----- --                 --

csakitt        <=    szöveg

értékegyforma  ==    1                  1

értékkülönböző <>    bla                kakukk

hashegyforma   ==    {key1, Key2}       {key1, Key2}

hashkülönböző  <>    {keykettő, keyegy} {keyegy, keyhárom}

kétfajtasemmi  <>                       {}

semmi          ==

tömbegyforma   ==    {1, 2, 3}          {1, 2, 3}

tömbkülönböző  <>    {a, b}             {c, b}

Az eredmény a várakozásunknak megfelelő lett! Mivel lehetne ezt még továbbfejleszteni? Vegyünk fel további paramétereket, amelyekkel lehet szűrni az eredményeket:

$includeequal : alapban az egyformákat nem jelenítjük meg, csak ha ezt a kapcsolót használjuk

$excludedifferent : alapban a különbözőket jelenítjük meg, de ezzel a kapcsolóval elrejthetjük azokat

$property : csak mely tulajdonságok között keressen

$exclude : mely tulajdonságokat hagyjon ki

Különösebben nem is magyarázom a 2. verziót, nézzük a függvényt:

function Compare-ObjectProperty2 {

param(

    $referenceobject,

    $differenceobject,

    [switch] $includeequal,

    [switch] $excludedifferent,

    [string[]] $property = "*",

    [string[]] $exclude

)

   

    $allprops = @()

    $rp = @()

    $dp = @()

    $rs = "r:"

    $ds = "d:"

 

    if($referenceobject){

        $rp = $referenceobject.psobject.Properties |

                Select-Object -ExpandProperty Name

        $allprops = @($rp)

        $rs += $referenceobject.tostring()

    }

 

    if($differenceobject){

        $dp = $differenceobject.psobject.Properties |

                Select-Object -ExpandProperty Name

        foreach($p in $dp){

            if($allprops -notcontains $p){

                $allprops += $p

            }

        }

        $ds += $differenceobject.tostring()

    }

 

    $allprops = $allprops | Where-Object {

            $pp = $_

            ($property | Where-Object {$pp -like $_}) -and !($exclude | Where-Object {$pp -like $_})

        } | Sort-Object

 

    foreach($p in ($allprops | Sort-Object)){

        $ra = $referenceobject.$p

        $da = $differenceobject.$p

        $rtype = if($null -eq $ra){"NULL"}else{$ra.gettype().fullname}

        $dtype = if($null -eq $da){"NULL"}else{$da.gettype().fullname}

 

        if($null -eq $ra -and $null -ne $da){

            $equal = "=>"

        }

        elseif($null -ne $ra -and $null -eq $da){

            $equal = "<="

        }

        elseif($rtype -ne $dtype){

            $equal = "<>"

        }

        elseif($ra -is [collections.ilist]){

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da){

                $equal = "<>"

            } 

            else{

                $equal = "=="

            }

        }

        elseif($ra -is [collections.idictionary]){

            $ra = @($ra.getenumerator())

            $da = @($da.getenumerator())

 

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da -Property Key, Value){

                $equal = "<>"

            }

            else{

                $equal = "=="

            }

        }

        else{

            $equal = if($ra -eq $da){"=="}else{"<>"}

        }

 

        if((!$excludedifferent -and $equal -ne '==') -or ($includeequal -and $equal -eq '==')){

            [pscustomobject] @{

                Property = $p

                Equal = $equal

                $rs = $referenceobject.$p

                $ds = $differenceobject.$p

            }

        }

    }

}

 Nézzük, ez hogyan működik:

PS C:\> Compare-ObjectProperty2 -referenceobject $o1 -differenceobject $o2

 

Property       Equal r:                 d:

--------       ----- --                 --

csakitt        <=    szöveg

értékkülönböző <>    bla                kakukk

hashkülönböző  <>    {keykettő, keyegy} {keyegy, keyhárom}

kétfajtasemmi  <>                       {}

tömbkülönböző  <>    {a, b}             {c, b}

Ebben a 2. verzióban tehát alapban csak a különbözőségek látszanak, vegyük hozzá az egyformákat is:

PS C:\> Compare-ObjectProperty2 -referenceobject $o1 -differenceobject $o2 -inc

ludeequal

 

Property       Equal r:                 d:

--------       ----- --                 --

csakitt        <=    szöveg

értékegyforma  ==    1                  1

értékkülönböző <>    bla                kakukk

hashegyforma   ==    {key1, Key2}       {key1, Key2}

hashkülönböző  <>    {keykettő, keyegy} {keyegy, keyhárom}

kétfajtasemmi  <>                       {}

semmi          ==

tömbegyforma   ==    {1, 2, 3}          {1, 2, 3}

tömbkülönböző  <>    {a, b}             {c, b}

Szűkítsük le az eredményeket az „é” vagy „t” betűkkel kezdődő tulajdonságokra, de azért a dupla „k” betűt tartalmazókat vegyük ki:

PS C:\> Compare-ObjectProperty2 -referenceobject $o1 -differenceobject $o2 -inc

ludeequal -property é*, t* -exclude *kk*

 

Property      Equal        r:        d:

--------      -----        --        --

értékegyforma ==            1         1

tömbegyforma  ==    {1, 2, 3} {1, 2, 3}

tömbkülönböző <>       {a, b}    {c, b}

A végső verzióban vezessünk be egy újabb szűrési lehetőséget, ahol elrejtjük a „semmit” tartalmazó sorokat. Semmi szerepelhet az egyik objektumnál, vagy mindkettőnél, vegy lehet olyan is, hogy pont a semmiket tartalmazó sorokat akarjuk megjeleníteni. Itt a „semmi” nem csak a tényleges $null-t, hanem az üres sztringet, üres tömböt, üres hashtáblát és a [system.dbnull]-t is jelentheti. Azaz az új paraméter legyen az, hogy $hide, ennek lehetséges értékei lehetnek: 'None', 'Empty', 'NonEmpty', 'BothEmpty', alapban az értéke ’None’, azaz nem rejtünk el semmit. Ezt a kiterjesztett „semmit” ki kell számolni, úgyhogy a függvényünk ezekkel a részekkel bővül, plusz még kiszűröm az AliasProperty típusú tulajdonságokat, valamint a „=>” és „<=” eredményeket rendelem azokhoz az esetekhez, ahol az egyik tulajdonság ezen kiterjesztett „semmi”, de a másik nem az:

function Compare-ObjectProperty {

param(

    $referenceobject,

    $differenceobject,

    [switch] $includeequal,

    [switch] $excludedifferent,

    [string[]] $property = "*",

    [string[]] $exclude,

    [ValidateSet('None','Empty','NonEmpty','BothEmpty')] $hide = 'None'

)

 

    $allprops = @()

    $rp = @()

    $dp = @()

    $rs = 'r:'

    $ds = 'd:'

 

    if($null -ne $referenceobject){

        $rp = $referenceobject.psobject.Properties |

                Where-Object {$_.membertype -ne 'AliasProperty'} |

                    Select-Object -ExpandProperty Name

        $allprops = @($rp)

        $rs = "r:" + $referenceobject.tostring()

    }

 

    if($null -ne $differenceobject){

        $dp = $differenceobject.psobject.Properties |

                Where-Object {$_.membertype -ne 'AliasProperty'} |

                    Select-Object -ExpandProperty Name

        foreach($p in $dp){

            if($allprops -notcontains $p){

                $allprops += $p

            }

        }

        $ds = "d:" + $differenceobject.tostring()

    }

 

    $allprops = $allprops | Where-Object {

            $pp = $_

            ($property | Where-Object {$pp -like $_}) -and !($exclude | Where-Object {$pp -like $_})

        } | Sort-Object

 

    foreach($p in $allprops){

        $ra = $referenceobject.$p

        $da = $differenceobject.$p

        $raempty = $null -eq $ra -or

                    '' -eq $ra -or

                    (($ra -is [collections.ilist] -or $ra -is [Collections.IDictionary]) -and $ra.count -eq 0) -or

                    $ra -is [System.DBNull]

        $daempty = $null -eq $da -or

                    '' -eq $da -or

                    (($da -is [collections.ilist] -or $da -is [Collections.IDictionary]) -and $da.count -eq 0) -or

                    $ra -is [System.DBNull]

        $rtype = if($null -eq $ra){"NULL"}else{$ra.gettype().fullname}

        $dtype = if($null -eq $da){"NULL"}else{$da.gettype().fullname}

 

        if($hide -eq 'Empty' -and ($raempty -or $daempty)){

            continue

        }

        elseif($hide -eq 'BothEmpty' -and $raempty -and $daempty){

            continue

        }

        elseif($hide -eq 'NonEmpty' -and (!$raempty -or !$daempty)){

            continue

        }

 

        if(($raempty -and !$daempty) -or ($dp -contains $p -and $rp -notcontains $p)){

            $equal = "=>"

        }

        elseif((!$raempty -and $daempty) -or ($rp -contains $p -and $dp -notcontains $p)){

            $equal = "<="

        }

        elseif($rtype -ne $dtype){

            $equal = "<>"

        }

        elseif($ra -is [collections.ilist]){

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da){

                $equal = "=="

            }

            else{

                $equal = "<>"

            }

        }

        elseif($ra -is [collections.idictionary]){

            $ra = @($ra.GetEnumerator())

            $da = @($da.GetEnumerator())

            if(Compare-Object -ReferenceObject $ra -DifferenceObject $da -Property Key, Value){

                $equal = "<>"

            }

            else{

                $equal = "=="

            }

        }           

        else{

            $equal = if($ra -eq $da){"=="}else{"<>"}

        }

 

        if((!$excludedifferent -and $equal -ne '==') -or ($includeequal -and $equal -eq '==')){

            [pscustomobject] @{

                Property = $p

                Equal = $equal

                $rs = $referenceobject.$p

                $ds = $differenceobject.$p

            }

        }

    }

}

 És akkor nézzünk néhány példát ennek a paraméternek a használatára is:

PS C:\> Compare-ObjectProperty -referenceobject $o1 -differenceobject $o2 -incl

udeequal -hide Empty

 

Property       Equal                 r:                 d:

--------       -----                 --                 --

értékegyforma  ==                     1                  1

értékkülönböző <>                   bla             kakukk

hashegyforma   ==          {key1, Key2}       {key1, Key2}

hashkülönböző  <>    {keykettő, keyegy} {keyegy, keyhárom}

tömbegyforma   <>             {1, 2, 3}          {1, 2, 3}

tömbkülönböző  ==                {a, b}             {c, b}

Itt a „-hide Empty” opciót választottam, és így az eredményből ki is maradtak azok a sorok, ahol az egyik vagy másik tulajdonság „semmi” volt. A következő példában meg pont azokat rejtem el, ahol egyik sem „semmi”:

PS C:\> Compare-ObjectProperty -referenceobject $o1 -differenceobject $o2 -incl

udeequal -hide NonEmpty

 

Property      Equal r: d:

--------      ----- -- --

kétfajtasemmi <>       {}

semmi         ==



Word To HTML Converter