Tom
Tom

Reputation: 501

Powershell: Filter Hashtable - and get back a Hastable

Filtering a Hashtable using GetEnumerator always returns a object[] instead of a Hashtable:

# Init Hashtable
$items = @{ a1 = 1; a2 = 2; b1 = 3; b2 = 4}
# apply a filter
$filtered = $items.GetEnumerator() | ?{ $_.Key -match "a.*" }
# The result looks great
$filtered
  Name                           Value
  ----                           -----
  a2                             2
  a1                             1
# … but it is not a Hashtable :-(
$filtered.GetType()
  IsPublic IsSerial Name                                     BaseType
  -------- -------- ----                                     --------
  True     True     Object[]                                 System.Array

Is there a nice solution to this problem?

Thanks a lot for any Help!, kind regards, Tom

Upvotes: 8

Views: 17288

Answers (4)

galeksandrp
galeksandrp

Reputation: 776

On a modern PowerShell (5+ as far as I remember) you can use reduce pattern. For that you need to use this form of ForEach-Object:

$Hashtable.Keys 
    | ForEach-Object -Begin { 
        $FilteredHashtable = @{} 
    } -Process {
        if ($_ -eq 'Example') {
            $FilteredHashtable[$_] = $Hashtable[$_];
        }
    } -End {
        $FilteredHashtable
    }

Yes, this snippet will return Hashtable.

Upvotes: 2

Justin Grote
Justin Grote

Reputation: 629

Here's an even simpler function, it even has include and exclude functionality

function Select-HashTable {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory,ValueFromPipeline)][Hashtable]$Hashtable,
        [String[]]$Include = ($HashTable.Keys),
        [String[]]$Exclude
    )

    if (-not $Include) {$Include = $HashTable.Keys}

    $filteredHashTable = @{}
    $HashTable.keys.where{
        $PSItem -in $Include
    }.where{
        $PSItem -notin $Exclude
    }.foreach{
        $filteredHashTable[$PSItem] = $HashTable[$PSItem]
    }
    return $FilteredHashTable
}

Examples:

$testHashtable = @{a=1;b=2;c=3;d=4}

$testHashTable | Select-HashTable -Include a

Name                           Value
----                           -----
a                              1
$testHashTable | Select-HashTable -Exclude b
Name                           Value
----                           -----
c                              3
d                              4
a                              1
$testHashTable | Select-HashTable -Include a,b,c -Exclude b


Name                           Value
----                           -----
a                              1
c                              3

Upvotes: 5

briantist
briantist

Reputation: 47802

$filtered is an array of dictionary entries. There's no single cast or ctor for this as far as I know.

You can construct a hash though:

$hash = @{}
$filtered | ForEach-Object { $hash.Add($_.Key, $_.Value) }

Another workflow:

# Init Hashtable
$items = @{ a1 = 1; a2 = 2; b1 = 3; b2 = 4}

# Copy keys to an array to avoid enumerating them directly on the hashtable
$keys = @($items.Keys)
# Remove elements not matching the expected pattern
$keys | ForEach-Object {
    if ($_ -notmatch "a.*") {
        $items.Remove($_)
    }
}

# $items is filtered

Upvotes: 14

Florian Feldhaus
Florian Feldhaus

Reputation: 5932

As the accepted answer was resulting in a BadEnumeration exception for me (but still worked), I modified it to not throw an exception and also made sure that the original HashTable is not modified by cloning it first:

# Init Hashtable
$items = @{ a1 = 1; a2 = 2; b1 = 3; b2 = 4}

$filtered = $items.Clone()

$items.Keys | ForEach-Object { 
    if ($_ -notmatch "a.*") { 
        $filtered.Remove($_) 
    }
}

Upvotes: 4

Related Questions