Powershell hashmap: nested filtering - Stack Overflow

admin2025-04-16  7

I’m trying create some automations to help with general activities on an environment and was trying to think of a clean accessible way to store and access info via powershell hashmaps

$test=@{
    "server1"=@{
        "service1"=@{
            "filePath"="C:\hi";
            "serviceName"="servName1";
            "processName"="procName1" ;
            "group"=@("ALL", "SUBSET" );
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
        "service2"=@{
            "filePath"="C:\hi2";
            "serviceName"="servName2";
            "processName"="procName2" ;
            "group"=@("ALL");
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
    "server2"=@{
        "service3"=@{
            "filePath"="C:\hi3";
            "serviceName"="servName3";
            "processName"="procName3" ;
            "group"=@("ALL", "SUBSET" )
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
};

Above lets me get list of servers, services on a specific server, etc easily but more importantly it gives an easy way to return a filter-down hashmap to get all info e.g. on a particular server etc

write-host "list of servers:";
$test.keys;
write-host "list of services on server1:";
$test['server1'].keys;

write-host "hashmap of services on server1:";
$test['server1'];

list of servers:
server2
server1

list of services on server1:
service2
service1

hashmap of services on server1:
Name                           Value
----                           -----
service2                       {serviceName, processName, properties1, group…}
service1                       {serviceName, processName, properties1, group…}

hashmap of SUBSET services:
server2                        {service3}
server1                        {service1}

I’m a little unsure if there is an elegant way to do more tailored filters that are nested much deeper e.g get the hashmap filtered down to only services that have SUBSET in the group name etc

write-host "hashmap of SUBSET services:";
#expected result
@{
    "server1"=@{
        "service1"=@{
            "filePath"="C:\hi";
            "serviceName"="servName1";
            "processName"="procName1" ;
            "group"=@("ALL", "SUBSET" );
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
    "server2"=@{
        "service3"=@{
            "filePath"="C:\hi3";
            "serviceName"="servName3";
            "processName"="procName3" ;
            "group"=@("ALL", "SUBSET" )
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
};

Only way I can think of is just a huge clunky series of nested for loops which might make it difficult to reuse if I want to do other filters like get hashmap of everything that has a specific serviceName.

Does anyone have any idea? If I can easily access $test['server1'] without a first level for loop, is there a way I can do the same thing for nested fields e.g.

$test(*)(*)[“group”].contains(“SUBSET”)

I’m trying create some automations to help with general activities on an environment and was trying to think of a clean accessible way to store and access info via powershell hashmaps

$test=@{
    "server1"=@{
        "service1"=@{
            "filePath"="C:\hi";
            "serviceName"="servName1";
            "processName"="procName1" ;
            "group"=@("ALL", "SUBSET" );
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
        "service2"=@{
            "filePath"="C:\hi2";
            "serviceName"="servName2";
            "processName"="procName2" ;
            "group"=@("ALL");
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
    "server2"=@{
        "service3"=@{
            "filePath"="C:\hi3";
            "serviceName"="servName3";
            "processName"="procName3" ;
            "group"=@("ALL", "SUBSET" )
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
};

Above lets me get list of servers, services on a specific server, etc easily but more importantly it gives an easy way to return a filter-down hashmap to get all info e.g. on a particular server etc

write-host "list of servers:";
$test.keys;
write-host "list of services on server1:";
$test['server1'].keys;

write-host "hashmap of services on server1:";
$test['server1'];

list of servers:
server2
server1

list of services on server1:
service2
service1

hashmap of services on server1:
Name                           Value
----                           -----
service2                       {serviceName, processName, properties1, group…}
service1                       {serviceName, processName, properties1, group…}

hashmap of SUBSET services:
server2                        {service3}
server1                        {service1}

I’m a little unsure if there is an elegant way to do more tailored filters that are nested much deeper e.g get the hashmap filtered down to only services that have SUBSET in the group name etc

write-host "hashmap of SUBSET services:";
#expected result
@{
    "server1"=@{
        "service1"=@{
            "filePath"="C:\hi";
            "serviceName"="servName1";
            "processName"="procName1" ;
            "group"=@("ALL", "SUBSET" );
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
    "server2"=@{
        "service3"=@{
            "filePath"="C:\hi3";
            "serviceName"="servName3";
            "processName"="procName3" ;
            "group"=@("ALL", "SUBSET" )
            "properties1"=@{"prop1"="prop2";};
            "properties2"=@{"prop1"="prop2";};
        };
    };
};

Only way I can think of is just a huge clunky series of nested for loops which might make it difficult to reuse if I want to do other filters like get hashmap of everything that has a specific serviceName.

Does anyone have any idea? If I can easily access $test['server1'] without a first level for loop, is there a way I can do the same thing for nested fields e.g.

$test(*)(*)[“group”].contains(“SUBSET”)
Share edited Feb 2 at 16:50 iRon 24k10 gold badges58 silver badges99 bronze badges asked Feb 2 at 3:21 CaptainObvCaptainObv 4341 gold badge6 silver badges15 bronze badges 3
  • 1 You mean something like this: $test.Keys | Where-Object { $test.$_.Values.group -contains 'SUBSET' } ? P.S. This is PowerShell, you don't need all those semi-colons – Theo Commented Feb 2 at 11:35
  • @Theo was looking more to return a filtered hash map (where service2 in server1 is removed. Fair enough with the semi colons :p – CaptainObv Commented Feb 2 at 11:58
  • Its an external solution, but you might have a look at: ObjectGraphTools: $Test | Get-Node '*.*.group' | Where Value -Contains SUBSET. As an aside: avoid semicolons as line terminators and avoid enclosing strings with smart quotes – iRon Commented Feb 2 at 16:50
Add a comment  | 

2 Answers 2

Reset to default 0

Thanks for the extra info in your comment.
If you want a filtered result where only services are listed that have keyword 'SUBSET' as one of the values in their 'group' item array, you could do like below:

$filtered = @{}                      # a new Hashtable object to store the results
foreach ($server in $test.Keys) {    # loop over the items in the original Hash
    foreach ($service in $test.$server.Keys) {
        if ($test.$server.$service.group -contains 'SUBSET') {
             $filtered.Add($server,@{$service = $test.$server.$service})
        }
    }
}

Now, $filtered contains only Hashtables of servers and their services containing 'SUBSET' in the group item:

Name                           Value
----                           -----
server1                        {service1}
server2                        {service3}

It's not exactly what you're looking for, but you can extend the Hashtable type, adding a ScriptMethod that can help you find nodes in your nested hashtable based on a delegate:

Update-TypeData -TypeName Hashtable -MemberType ScriptMethod -MemberName Find -Value {
    param([System.Func[object, object, bool]] $Delegate)

    $stack = [System.Collections.Stack]::new()
    $stack.Push([pscustomobject]@{ Node = $this })

    while ($stack.Count) {
        $dict = $stack.Pop()
        foreach ($pair in $dict.Node.GetEnumerator()) {
            $node = [pscustomobject]@{
                Path   = $dict.Path + "[$($pair.Key)]"
                Node   = $pair.Value
                Parent = $dict
            }

            if ($pair.Value -is [System.Collections.IDictionary]) {
                $stack.Push($node)
                continue
            }

            try {
                if ($Delegate.Invoke($pair.Key, $pair.Value)) {
                    $node
                }
            }
            catch { }
        }
    }
}

The usage would be:

$test.Find({param($key, $value) $value.Contains('SUBSET') })

# Path                       Node          Parent
# ----                       ----          ------
# [server1][service1][group] {ALL, SUBSET} @{Path=[server1][service1]; Node=System.Co...
# [server2][service3][group] {ALL, SUBSET} @{Path=[server2][service3]; Node=System.Co...

$test.Find({param($key, $value) $value -eq 'C:\hi3' })

# Path                          Node   Parent
# ----                          ----   ------
# [server2][service3][filePath] C:\hi3 @{Path=[server2][service3]; Node=System.Collec...

$test.Find({param($key, $value) $key -eq 'serviceName' -and $value -eq 'servName2' })

# Path                             Node      Parent
# ----                             ----      ------
# [server1][service2][serviceName] servName2 @{Path=[server1][service2]; Node=System....

Then using the .Parent property you can navigate up in the tree, for example:

$result = $test.Find({param($key, $value) $value.Contains('SUBSET') })
$result[0].Parent.Parent.Node

# Name       Value
# ----       -----
# service1   {[properties1, System.Collections.Hashtable], [filePath, C:\hi],  ...
# service2   {[properties1, System.Collections.Hashtable], [filePath, C:\hi2], ...
转载请注明原文地址:http://anycun.com/QandA/1744810197a87932.html