none
Help on using hashtables with sub structures inside RRS feed

  • Question

  • Hi everyone,

    I'm currently working on a script that scans Active Directory for computers, and lists current users logged, and logs to an hashtable the username as key, and a count as a value.

    After that it sends an email with the list. This part is all developed and working. Now I want to go further, and in the hash table, I would like to have as the key the username, and as the value the counter and a list of the computer names where the user is logged on.

    I have this code for working with the hashtable:

    ForEach ($c in $computer) {
            #Get explorer.exe processes
            $proc = gwmi win32_process -computer $c -Filter "Name = 'explorer.exe'"
            #Go through collection of processes
            ForEach ($p in $proc) {
                $temp = "" | Select Computer, Domain, User
                $temp.computer = $c
                $temp.user = ($p.GetOwner()).User
                $temp.domain = ($p.GetOwner()).Domain
                $report += $temp
                If($UsersList.ContainsKey($temp.user) -eq 1){
                    $UsersList[$temp.user] = $UsersList[$temp.user] + 1
                    #$UsersList
                    }
                    Else {
                        $UsersList.add($temp.user,1)
                    }
              }
            }

    Can I have it the way I'd like, or have to change my approach?

    Thanks in advance


    Nuno Silva

    Tuesday, October 28, 2014 2:31 PM

Answers

  • Yep, you can do that.  You would just create an object (or another hashtable) stored in the value of the $UsersList hashtable.  Something like this:

    ForEach ($c in $computer) {
        #Get explorer.exe processes
        $proc = gwmi win32_process -computer $c -Filter "Name = 'explorer.exe'"
        #Go through collection of processes
        ForEach ($p in $proc) {
            $temp = "" | Select Computer, Domain, User
            $temp.computer = $c
            $temp.user = ($p.GetOwner()).User
            $temp.domain = ($p.GetOwner()).Domain
            $report += $temp
    
            $object = $UsersList[$temp.user]
            if ($null -eq $object)
            {
                $object = New-Object psobject -Property @{
                    Count = 0
                    ComputerList = New-Object System.Collections.ArrayList
                }
    
                $UsersList.Add($temp.user, $object)
            }
    
            $object.Count++
            $null = $object.ComputerList.Add($temp.computer)
        }
    }
    

    Tuesday, October 28, 2014 2:47 PM
  • You've got two problems there.  You'd need to change {3} to {2}, since you're only passing in 3 arguments to the format operator (indexes 0, 1, and 2).  Also, as-is, you'd just wind up seeing "System.Collections.ArrayList" for the values in your string; you need to join them into a string first.  Try this:

    ($UsersList.GetEnumerator() |
        Sort-Object {$_.Value.Count} -descending |
        % { "{0}, {1} - {2}" -f $_.key, $_.value.Count, ($_.value.ComputerList -join ', ') })
    

    Tuesday, October 28, 2014 4:09 PM

All replies

  • Yep, you can do that.  You would just create an object (or another hashtable) stored in the value of the $UsersList hashtable.  Something like this:

    ForEach ($c in $computer) {
        #Get explorer.exe processes
        $proc = gwmi win32_process -computer $c -Filter "Name = 'explorer.exe'"
        #Go through collection of processes
        ForEach ($p in $proc) {
            $temp = "" | Select Computer, Domain, User
            $temp.computer = $c
            $temp.user = ($p.GetOwner()).User
            $temp.domain = ($p.GetOwner()).Domain
            $report += $temp
    
            $object = $UsersList[$temp.user]
            if ($null -eq $object)
            {
                $object = New-Object psobject -Property @{
                    Count = 0
                    ComputerList = New-Object System.Collections.ArrayList
                }
    
                $UsersList.Add($temp.user, $object)
            }
    
            $object.Count++
            $null = $object.ComputerList.Add($temp.computer)
        }
    }
    

    Tuesday, October 28, 2014 2:47 PM
  • Thanks for the help. One more question regarding printing the arraylist.

    I use the code below to print everything. I can print successfully the key and value but get an error on the computerlist.

    ($UsersList.GetEnumerator() |
        Sort-Object Value -descending |
        % { "{0}, {1} - {3}" -f $_.key, $_.value.Count, $_.value.ComputerList })
    
    


    Error formatting a string: Index (zero based) must be greater than or equal to zero and less than the size of the argument list..

    +     % { "{0}, {1} - {3}" -f $_.key, $_.value.Count, $_.value.ComputerList[0] })
    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: ({0}, {1} - {3}:String) [], RuntimeException
        + FullyQualifiedErrorId : FormatError
     
    Error formatting a string: Index (zero based) must be greater than or equal to zero and less than the size of the argument list..

    How can I print it out?

    Thanks in advance,


    Nuno Silva

    Tuesday, October 28, 2014 3:55 PM
  • You've got two problems there.  You'd need to change {3} to {2}, since you're only passing in 3 arguments to the format operator (indexes 0, 1, and 2).  Also, as-is, you'd just wind up seeing "System.Collections.ArrayList" for the values in your string; you need to join them into a string first.  Try this:

    ($UsersList.GetEnumerator() |
        Sort-Object {$_.Value.Count} -descending |
        % { "{0}, {1} - {2}" -f $_.key, $_.value.Count, ($_.value.ComputerList -join ', ') })
    

    Tuesday, October 28, 2014 4:09 PM
  • David thanks.

    I had actually noticed that typo, and it got it solved immediately. Now I've got the script working and testing! 

    Thanks once more.


    Nuno Silva

    Tuesday, October 28, 2014 4:11 PM