pf-HashTable.ps1

function Update-HashTable_Swap_KeyValue([HashTable]$HashTable) {
    $result = @{}
    $HashTable.GetEnumerator() | ForEach-Object { $result.Add($_.Value, $_.Key) }
    $result
}
function Update-HashTable_Swap_KeyValue:::Test {
    Update-HashTable_Swap_KeyValue @{ a = '1' } | assert @{ '1' = 'a' }
}

function ConvertFrom-PSObject_ToHashtable
{
    param (
        [Parameter(ValueFromPipeline)]
        $InputObject
    )

    process
    {
        if ($null -eq $InputObject) { return $null }

        if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string])
        {
            $collection = @(
                foreach ($object in $InputObject) { ConvertFrom-PSObject_ToHashtable $object }
            )

            Write-Output -NoEnumerate $collection
        }
        elseif ($InputObject -is [psobject])
        {
            $hash = @{}

            foreach ($property in $InputObject.PSObject.Properties)
            {
                $hash[$property.Name] = ConvertFrom-PSObject_ToHashtable $property.Value
            }

            $hash
        }
        else
        {
            $InputObject
        }
    }
}

function ConvertTo-HashTable($keyProp, $valueProp) {
    begin {
        $result = [Ordered]@{}
    }
    process {
        $current = $_
        if (-not $current) { return }
        $key = if ($keyProp) { $current.$keyProp } else { $current }
        $value = if ($valueProp) { $current.$valueProp } else { $current }
        $result.Add($key,$value)
    }
    end {
        return $result
    }
}
function ConvertTo-HashTable:::Example {
    [PSCustomObject]@{A = 1; B = 2} | ConvertTo-HashTable -keyProp A -valueProp B
}

function Remove-HashTable ([HashTable]$left, [HashTable]$right) {
    [HashTable]$result = New-Object HashTable
    foreach ($key in $left.Keys) {
        if ( $right[$key] -ne $left[$key] ) {
            $result[$key] = $left[$key]
        }
    }
    return $result
}
function Remove-HashTable:::Test {
    Remove-HashTable @{'K1' = 1; 'K2' = 2; 'K3' = 3} @{'K1' = 1 } | Assert @{'K2' = 2; 'K3' = 3 }
    Remove-HashTable @{'K1' = 1; 'K2' = 2; 'K3' = 3} @{'K1' = 2 } | Assert @{'K1' = 1; 'K2' = 2; 'K3' = 3}
}

function Remove-HashTable_Values ([HashTable]$InputObject, $values = @('',$null)) {
    $keyList = @() + $InputObject.Keys
    foreach ( $key in $keyList ) {
        if ( $InputObject[$key] -in $values ) {
            $InputObject.Remove($key)
        }
    }
}

function Test-HashTable {
    Param(
        [Parameter(ValueFromPipeline=$true)]
        $value
    )
    process {
        if (-not $value) {
            return $false
        }
        if ( ( $value -is [HashTable]) -or ( $value -is [System.Collections.Specialized.OrderedDictionary]) ) {
            return $true
        }
        return $false
    }
}

function ConvertFrom-HastTable_ToCollection {
    Param(
        [Parameter(ValueFromPipeline=$true)]
        $inputObject = @{},
        $keyProp = 'Key', 
        $valueProp = 'Value'
    )
    process {
        foreach ($key in ( $inputObject.Keys | Sort-Object ) ) {
            $value = $inputObject[$key]

            if ( Test-HashTable $value ) {
                $value = ConvertFrom-HastTable_ToCollection -inputObject $value
            }
        
            [PSCustomObject]@{$keyProp = $key; $valueProp = $value }
        }
    }
}
function ConvertFrom-HastTable_ToCollection:::Test {
    $a1 = ConvertFrom-HastTable_ToCollection @{'A' = 1; S = @{'A' = 1} }
    $b1 = @{'A' = 2; S = @{'A' = 1} } | ConvertFrom-HastTable_ToCollection 
    $c1 = [Ordered]@{'A' = 2; S = @{'A' = 1} } | ConvertFrom-HastTable_ToCollection 
}

function ConvertFrom-HastTable_ToObject{
    Param(
        [Parameter(ValueFromPipeline=$true)]
        $inputObject = @{}
    )
    process {
        $result = New-Object –TypeName PSObject
        ConvertFrom-HastTable_ToCollection $inputObject | ForEach-Object {
            $value = $_.value
            if ( Test-HashTable $value] ) {
                $value = ConvertFrom-HastTable_ToObject -inputObject $value
            }
            $result | Add-Member –MemberType NoteProperty –Name $_.key –Value $value
        }
        $result
    }
}
function ConvertFrom-HastTable_ToObject:::Example {
    ConvertFrom-HastTable_ToObject @{'A' = 1; S = @{'A' = 1} }
    @{'A' = 2; S = @{'A' = 2} } | ConvertFrom-HastTable_ToObject 
}

function Remove-EmptyValues {
    process {
        [HashTable]$current = $_
        $result = @{}
        foreach ($pair in $current.GetEnumerator() ) {
            if ( $null -ne $pair.Value ) {
                $result.Add($pair.Name, $pair.Value)
            }
        }
        $result
    } 
}
function Remove-EmptyValues:::Test {
    @{a = 1} | Remove-EmptyValues | assert @{a = 1}
    @{a = $null} | Remove-EmptyValues | assert @{}
    @{a = $null; b = 2} | Remove-EmptyValues | assert @{b = 2}
    $value = @{a = $null; b = 2}
    $value = $value | Remove-EmptyValues
}

function New-HashTable_Clone {
    Param (
        [HashTable]$source
    )        
    $result = @{}
    if (-not $source) {
        return $result
    }
    $source.Clone()
}

function Compare-HashTable {
    Param (
        [HashTable]$left,
        [HashTable]$right,
        [Switch]$ByValue
    )

    $left = @{ A = 1; B = 2 }
    $right = @{ A = 1; B = 3 }

    $l = ConvertFrom-HastTable_ToCollection $left
    $r = ConvertFrom-HastTable_ToCollection $right
    Compare-Object -ReferenceObject $l -DifferenceObject $r -Property 


    if (-not $right) { return if ( $left -ne $null ) { $left.Clone() } else { @{} } }

    foreach ($pair in $right.GetEnumerator() ) {
        $left.Contains($pair.Key)
        if ( $null -ne $pair.Key ) {
            $result.Add($pair.Name, $pair.Value)
        }
    }
}

function Get-HashTable_Flat {
    Param(
        [Parameter(ValueFromPipeline=$True)]
        [HashTable]$hashtable,
        [string]$prefix,
        [System.Collections.Generic.ISet[object]]
        $visited = ( New-Object 'System.Collections.Generic.HashSet[object]' )
    )
    process {
        if ( ( -not $hashtable ) -or $visited.Contains($hashtable) ) {
            return 
        }
        $visited.Add($hashtable) | Out-Null
        foreach ($pair in $hashtable.GetEnumerator()) {
            if ( $prefix ) {
                $subPrefix = $prefix + "." + $pair.Name
            }
            else {
                $subPrefix = $pair.Name
            }
            if ( $pair.Value -is [HashTable] ) {
                Get-HashTable_Flat -prefix $subPrefix -hashtable $pair.Value -visited $visited
            }
            else {
                [PSCustomObject]@{ Path = $subPrefix; Value = $pair.Value}
            }
        }
    }
}
function Get-HashTable_Flat:::Test {
    $value = @{ 
        A = @{ 
            A_F = 1; 
            A_G = $null
        };
        B = @{ 
            B_L = 1; 
            B_M = 2
        };
    }
    $result = $value | Get-HashTable_Flat -prefix '$Value'
    $result
}