functions/Select-ExceptIn.ps1
# <copyright file="Select-ExceptIn.ps1" company="Endjin Limited"> # Copyright (c) Endjin Limited. All rights reserved. # </copyright> <# .SYNOPSIS Compares two Hashtable arrays and returns the hashtable entries from the input that don't exist in the reference array. .DESCRIPTION This is intended to find hashtable-based objects that are missing from the reference array of hashtables. For example, as part of some synchronisation logic. NOTE: This function does not support nested hashtables as it relies on Compare-Object for the comparison of each hashtable key. .NOTES This function's primary use-case is to support determining how a 'desired state' differs from a 'current state'. For example, comparing the current Azure resource manager permissions with those defined in configuration. In this case the the raw objects cannot be compared as they have different properties - the current state will be available as the 'Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment' type, whilst the configuration may defined with a much simpler hashtable structure. Therefore the usage of this function assumes that custom projections will be created that map the current and desired states into a common structure and it is those projections that get passed to this function. It is then the responsibility of the consumer to interpret the results for their particular scenario. .PARAMETER InputObject The array of hashtables that should exist in the reference array. .PARAMETER ReferenceArray The array of hashtables to be compared with the input array. .OUTPUTS An array of hashtables representing the input hashtables that do not exist in the reference array. .EXAMPLE $input = @( @{ Name="foo"; Id=1 } @{ Name="foobar"; Id=3 } ) $reference = @( @{ Name="bar"; Id=100 } @{ Name="foobar"; Id=3 } ) $input | Select-ExceptIn $reference The output would be: @( @{ Name="foo"; Id=1 } ) #> function Select-ExceptIn { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [AllowEmptyCollection()] [Hashtable[]] $InputObject, [Parameter(Mandatory = $true, Position = 0)] [AllowEmptyCollection()] [Hashtable[]] $ReferenceArray ) Begin { $results = @() } Process { foreach ($inputItem in $InputObject) { $inputItemToCompare = _sortedHashtable($inputItem) # Assume the item is missing unless we find a match $isMissing = $true foreach ($referenceItem in $ReferenceArray) { $refItemToCompare = _sortedHashtable($referenceItem) $keysDiff = Compare-Object $refItemToCompare.Keys $inputItemToCompare.Keys $valuesDiff = Compare-Object $refItemToCompare.Values $inputItemToCompare.Values if (!$keysDiff -and !$valuesDiff) { # When the comparisons return null then we have found a hashtable in the # reference array that exactly matches the input item currently being # processed. # Record that it is not missing and abandon this inner loop $isMissing = $false break } } if ($isMissing) { $results += $inputItem } } } End { @(,$results) } } function _sortedHashtable { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [hashtable] $Hashtable ) $sortedHashtable = [ordered]@{} $Hashtable.Keys | Sort-Object | ForEach-Object { $sortedHashtable.Add($_, $Hashtable[$_]) } $sortedHashtable } |