Reputation: 501
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
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
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
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
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