Skip to content

Instantly share code, notes, and snippets.

@dbroeglin
Last active June 28, 2022 09:28
Show Gist options
  • Save dbroeglin/c6ce3e4639979fa250cf to your computer and use it in GitHub Desktop.
Save dbroeglin/c6ce3e4639979fa250cf to your computer and use it in GitHub Desktop.
A simple Compare-Hashtable function for powershell
$ErrorActionPreference = "Stop"
function Compare-Hashtable {
<#
.SYNOPSIS
Compare two Hashtable and returns an array of differences.
.DESCRIPTION
The Compare-Hashtable function computes differences between two Hashtables. Results are returned as
an array of objects with the properties: "key" (the name of the key that caused a difference),
"side" (one of "<=", "!=" or "=>"), "lvalue" an "rvalue" (resp. the left and right value
associated with the key).
.PARAMETER left
The left hand side Hashtable to compare.
.PARAMETER right
The right hand side Hashtable to compare.
.EXAMPLE
Returns a difference for ("3 <="), c (3 "!=" 4) and e ("=>" 5).
Compare-Hashtable @{ a = 1; b = 2; c = 3 } @{ b = 2; c = 4; e = 5}
.EXAMPLE
Returns a difference for a ("3 <="), c (3 "!=" 4), e ("=>" 5) and g (6 "<=").
$left = @{ a = 1; b = 2; c = 3; f = $Null; g = 6 }
$right = @{ b = 2; c = 4; e = 5; f = $Null; g = $Null }
Compare-Hashtable $left $right
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[Hashtable]$Left,
[Parameter(Mandatory = $true)]
[Hashtable]$Right
)
function New-Result($Key, $LValue, $Side, $RValue) {
New-Object -Type PSObject -Property @{
key = $Key
lvalue = $LValue
rvalue = $RValue
side = $Side
}
}
[Object[]]$Results = $Left.Keys | % {
if ($Left.ContainsKey($_) -and !$Right.ContainsKey($_)) {
New-Result $_ $Left[$_] "<=" $Null
} else {
$LValue, $RValue = $Left[$_], $Right[$_]
if ($LValue -ne $RValue) {
New-Result $_ $LValue "!=" $RValue
}
}
}
$Results += $Right.Keys | % {
if (!$Left.ContainsKey($_) -and $Right.ContainsKey($_)) {
New-Result $_ $Null "=>" $Right[$_]
}
}
$Results
}
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"
Describe "Compare-Hashtable" {
Context "When both are empty" {
$Left, $Right = @{}, @{}
It "should return nothing" {
Compare-Hashtable $Left $Right | Should BeNullOrEmpty
}
}
Context "When both have one identical entry" {
$Left, $Right = @{ a = "x" }, @{ a = "x" }
It "should return nothing" {
Compare-Hashtable $Left $Right | Should BeNullOrEmpty
}
}
Context "When left contains a key with a null value" {
$Left, $Right = @{ a = $Null }, @{}
It "should return nothing" {
[Object[]]$result = Compare-Hashtable $Left $Right
$result.Length | Should Be 1
$result.side | Should Be "<="
$result.lvalue | Should Be $Null
$result.rvalue | Should Be $Null
}
}
Context "When right contains a key with a null value" {
$Left, $Right = @{}, @{ a = $Null }
It "should return nothing" {
[Object[]]$result = Compare-Hashtable $Left $Right
$result.Length | Should Be 1
$result.side | Should Be "=>"
$result.lvalue | Should Be $Null
$result.rvalue | Should Be $Null
}
}
Context "When both contain various stuff " {
$Left = @{ a = 1; b = 2; c = 3; f = $Null; g = 6; k = $Null }
$Right = @{ b = 2; c = 4; e = 5; f = $Null; g = $Null; k = 7 }
$Result = Compare-Hashtable $Left $Right
It "should contain 5 differences" {
$Result.Length | Should Be 5
}
It "should return a: 1 <=" {
($Result | ? { $_.Key -eq "a" }).Side | Should Be "<="
($Result | ? { $_.Key -eq "a" }).LValue | Should Be 1
($Result | ? { $_.Key -eq "a" }).RValue | Should Be $Null
}
It "should return c: 3 <= 4" {
($Result | ? { $_.Key -eq "c" }).Side | Should Be "!="
($Result | ? { $_.Key -eq "c" }).LValue | Should Be 3
($Result | ? { $_.Key -eq "c" }).RValue | Should Be 4
}
It "should return e: <= 5" {
($Result | ? { $_.Key -eq "e" }).Side | Should Be "=>"
($Result | ? { $_.Key -eq "e" }).LValue | Should Be $Null
($Result | ? { $_.Key -eq "e" }).RValue | Should Be 5
}
It "should return g: 6 !=" {
($Result | ? { $_.Key -eq "g" }).Side | Should Be "!="
($Result | ? { $_.Key -eq "g" }).LValue | Should Be 6
($Result | ? { $_.Key -eq "g" }).RValue | Should Be $Null
}
It "should return k: != 7" {
($Result | ? { $_.Key -eq "k" }).Side | Should Be "!="
($Result | ? { $_.Key -eq "k" }).LValue | Should Be $Null
($Result | ? { $_.Key -eq "k" }).RValue | Should Be 7
}
}
}
@Fishy78
Copy link

Fishy78 commented Apr 14, 2020

Have added an option -includeEqual to have a full Hashtable comparisation

@bjtucker
Copy link

Thanks for sharing this @dbroeglin! Would you be willing to release this under an open-source license? If so, mentioning the license name in the comments would be enough for my purpose, and would be a huge help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment