How to Create Hash Table from String Data?

This topic contains 5 replies, has 3 voices, and was last updated by Profile photo of Patrick Wloch Patrick Wloch 2 years, 3 months ago.

  • Author
    Posts
  • #18182
    Profile photo of Patrick Wloch
    Patrick Wloch
    Participant

    Hoping to get some help converting strings into objects. I have a bunch of output that looks like the following:

    Sample
    ————————————————–
    LOGICAL UNIT NUMBER 68
    Name TIER2_LUN068
    Current owner: SP B

    LOGICAL UNIT NUMBER 110
    Name TIER1_LUN110
    Current owner: SP A

    LOGICAL UNIT NUMBER 203
    Name TIER1_LUN203
    Current owner: SP B

    LOGICAL UNIT NUMBER 81
    Name TIER1_LUN081
    Current owner: SP A
    ————————————————–
    End Sample

    I want to parse through the string output and bring each of the (3) properties into a hash table.

    The pseudo code of what I'm trying to accomplish is as follows:
    Gather the output into a variable ($getLuns)
    Loop through the output line by line, ($Line) and breaking it apart by blank spaces ($Parts)
    Search each part for specific value
    When found add the item to hash table (Name, value)

    My function doesn't appear to be working since the key value name is repeating. It gives the error that "Item has already been added"

    function Get-EMCLUNInfo { 
        [CmdletBinding()]
    
        Param([Parameter(Mandatory=$true)]
                  [string] $TAIP)
    
        Begin {
            $LHash = $null
            $LHash = @{}
            $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner
        }
    
         Process {
    	    foreach ($line in $GetLUNs){ 
                $parts = [regex]::Split($line, '\s+') 
    		    if($parts[0] -eq 'LOGICAL') {
    		        $LUN = [int]$parts[3]
                            $LHash.Add('LUN_ID',$LUN)
    		    } 
    		    if($parts[0] -eq 'Name') { 
    		        $LName = [string]::Join(' ', $parts[1..$($parts.Count)]) 
                            $LHash.Add('LUN_Name',$LName)		        
    		    }
                        if($parts[0] -eq 'Current') { 
    		        $SPID = $parts[3]
    		        $LHash.Add('SP',$SPID)
    	             }  
                 }
        }
    
        End {$LHash}
    }
    
    Get-EMCLUNInfo '192.168.8.97'
    
    

    Again I'm still new, so if there's a better way to accomplish (Maybe using a switch statement instead of if) please let me know. Thank you in advance for any and all help.

  • #18183
    Profile photo of Patrick Wloch
    Patrick Wloch
    Participant

    Anybody ideas out there on how to best do this?

  • #18201
    Profile photo of Patrick Wloch
    Patrick Wloch
    Participant

    OK, I've been messing with this seem to be getting closer....but still not fully there.

    Function Get-EMCLUNInfo {
        [CmdletBinding()]
    
        Param([Parameter(Mandatory=$true)]
                  [string] $TAIP)
    
        Begin {
            $LHash = $null
            $LHash = @{}
            $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner
        }
        Process {
            $GetLUNs | ForEach-Object {
                switch -Regex ($_) {
                    "LOGICAL*" { $obj = New-Object -TypeName psobject; $obj | Add-Member NoteProperty "LUN_ID" -Value ($_.split(" ")[3].trim()); break } 
    	        "Name*"    { $obj | Add-Member NoteProperty "LUN_Name" -Value  ($_.split(" ")[1].trim()); break } 
    		"Current*" { $obj | Add-Member NoteProperty "LUN_SP" -Value ($_.split(" ")[1..($_.Count)]); $LHash += $obj } 
    	     }  
            }     
        } 
        End {}
    }
    
    
    Get-EMCLUNInfo '192.168.8.97'
    

    Running this gives the error "A hash table can only be added to another hash table." Any help is greatly appreciated.

  • #18228
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    So, let's break out just creating a custom object. Although it works, using Add-Member IMHO is the most[b] inefficient[/b] way to build an object.

    Function Get-It {
        begin{
        #Generate a blank array to place objects into
        $LHash = @()
        }
        process{
            
            for($i=0;$i -le 5;$i++) {
                #The main issue is understanding that each "row" is an object,
                #so you are generating an object for each item processed
                $object = New-Object -TypeName psobject
                #Now there is a blank object with no properties, so
                #to add properties you are using Add-Member.
                $object|Add-Member NoteProperty 'Number' -Value $i
                #Now you have to append the row to the blank array
                $LHash += $object
            }
        }
        end{
            #Return your variable
            $LHash
        }
    }

    In the above example you are doing A LOT of work with mulitple variables to generate a single object. Here is the best way I've found (Thank you Dave) to create a object:

    Function Get-It {
        begin{
            #Generate a blank array to place objects into
            $object = @()
        }
        process{
            #Take all results from the for loop and add them to the blank array
            $object = for($i=0;$i -le 5;$i++) {
                #Create variables for all your individual results (e.g. your switch regex)
                $myValue = "AValue{0}" -f $i
                #Generate a new object and assign the appropriate variable to the property
                New-Object -TypeName PSObject -Property @{Number=($myValue)}
            }
        }
        end{
            #Return your variable
            $object
        }
    }

    Good uses for Add-Member would be adding a static (same value) to all rows at once like:

    PS C:\> $test = Get-It

    PS C:\> $test | Add-Member -MemberType NoteProperty -Name AnotherColumn -Value "AStaticValue"

    or adding Aliases to objects properties.

  • #18234
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    This type of list output looks fairly straightforward to parse. In your sample text, each new object begins with a LOGICAL UNIT NUMBER line, so we can use that line as the indicator to finish creating the previous object (if any), and start building a new one. The code might look something like this:

    function Get-EMCLUNInfo { 
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory=$true)]
            [string] $TAIP
        )
     
        $LHash = @{}
        $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner
    
        foreach ($line in $GetLUNs){ 
            switch -Regex ($line) {
                '^LOGICAL UNIT NUMBER (\d+)' {
                    if ($LHash.Count -gt 0) {
                        New-Object psobject -Property $LHash
                    }
    
                    $LHash = @{ LUN_ID = [int]$matches[1] }
                    break
                }
    
                '^Name\s*(.*)$' {
                    $LHash['LUN_Name'] = $matches[1]
                    break
                }
    
                '^Current owner:\s*SP\s*(.*)$' {
                    $LHash['SP'] = $matches[1]
                    break
                }
            }
        }
    
        if ($LHash.Count -gt 0) {
            New-Object psobject -Property $LHash
        }
    }
    
    • #18247
      Profile photo of Patrick Wloch
      Patrick Wloch
      Participant

      That worked! Thank you both for the input.

      Quick Question; What does the conditional statement at the end do?

      if ($LHash.Count -gt 0) {
      New-Object psobject -Property $LHash

      Also, I noticed that I can't sort based on LUN_ID, any idea how to fix that. When looking at Get-Member, it looks like the noteproperty's are not expandable (Not sure if that matters)

      TypeName: System.Management.Automation.PSCustomObject
      
      Name                 MemberType   Definition                         
      ----                       ----------              ----------                         
      Equals               Method              bool Equals(System.Object obj)     
      GetHashCode  Method              int GetHashCode()                  
      GetType              Method             type GetType()                     
      ToString             Method              string ToString()                  
      LUN_ID              NoteProperty    System.Int32 LUN_ID=1              
      LUN_Name       NoteProperty    System.String LUN_Name=TIER0_LUN001
      LUN_SP             NoteProperty    System.String LUN_SP=A
      

You must be logged in to reply to this topic.