Combining Different Objects into one Array

This topic contains 0 replies, has 1 voice, and was last updated by  Forums Archives 5 years, 9 months ago.

  • Author
    Posts
  • #5155

    by Brett.Osiewicz at 2012-09-30 06:47:13

    I am building an inventory script. As the script evolves, I predict it will produce objects with different properties that can be exported to single fileshare via Export-Clixml. In order to compare them, I will need to pull these various objects into an array. I can't believe that my problem is unique. If someone has already written a blog post, I would welcome that as well.

    My current strategy
    1. Compile a list of all properties in all objects.
    2. Remove the Duplicate Properties.
    3. Build an empty object with those properties.
    4. Build a collection by importing objects via a hash table. This is where I get stuck.


    $Nextobj = New-Object -TypeName psobject -Property @{OS="Windows7";Date=Get-Date;Serial='12345'}
    $Firstobj = New-Object -TypeName psobject -Property @{OS="Windows7";Date=Get-Date;Serial='34568';DriveSpace='120g'}
    $Lastobj = New-Object -TypeName psobject -Property @{OS="Windows7";Date=Get-Date;Serial='56789';IPAddress='10.10.10.10'}

    The following function builds an array of all Properties.

    function Select-Properties {
    param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $Obj )
    process {
    $Obj | ForEach-Object { $_.psobject.Properties | select -Property name }
    }}#end function

    But I only want the unique properties
    PS C:\Windows\system32> $Firstobj,$Nextobj,$Lastobj | Select-Properties | Select-Object -Property * -Unique

    Name
    —-
    Date
    Serial
    OS
    DriveSpace
    IPAddress

    by Brett.Osiewicz at 2012-09-30 08:33:50

    I kept working with it. I can go back to the original objects and select the properties from these objects whether they are there or not.
    However, my next question is fairly simple. How do I use that array of total properties and feed them into the final Select-Object on the Pipeline?

    PS C:\Windows\system32> $Nextobj,$Firstobj,$Lastobj | Select-Object -Property Date,Serial,OS,DriveSpace,IPAddress

    Date : 9/30/2012 11:15:10 AM
    Serial : 12345
    OS : Windows7
    DriveSpace :
    IPAddress :

    Date : 9/30/2012 11:15:10 AM
    Serial : 34568
    OS : Windows7
    DriveSpace : 120g
    IPAddress :

    Date : 9/30/2012 11:15:10 AM
    Serial : 56789
    OS : Windows7
    DriveSpace :
    IPAddress : 10.10.10.10

    by poshoholic at 2012-09-30 09:34:08

    Consider this approach instead:
    # Get two different objects
    $service = Get-Service wuauserv
    $process = Get-Process -Id $PID

    # Get all unique property names on those objects
    $propertyNames = $service,$process | ForEach-Object {$_.PSObject.Properties} | Select-Object -ExpandProperty Name -Unique

    # Now process our objects and emit custom objects with all properties
    $service,$process | Select-Object $propertyNames | ForEach-Object {
    # Before passing the objects on, let's set their type name so that they are all the same type all the time
    $_.PSTypeNames.Clear()
    $_.PSTypeNames.Add('InventoryRecord') # You can use whatever name you want to here.
    $_
    }

    by Brett.Osiewicz at 2012-09-30 10:00:40

    Cool thats its. I should have taken the clue of -expandproperty when it came up on Intellisense, and I never would have thought to rename the type of object. Thank you.

    by Brett.Osiewicz at 2012-09-30 17:01:25

    One more thing. I am still having trouble understanding how to feed parameters in and get information out from functions. The first function seems to work well. When I use your original logic, the logic works, but i can not insert it into the pipeline.

    I would assume that the pipeline method would mean these functions could be used as:
    $service,$process | Select-PropertyNames | New-InventoryArray | Out-GridView

    function Select-PropertyNames {
    param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)]$ObjectName )
    Begin{}
    Process {$AllPropertyNames = $_.psobject.Properties | Select-Object -ExpandProperty Name }
    End {$AllPropertyNames | Select-Object -Unique}
    }#end function

    function New-InventoryArray {
    param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)]$InventoryObject,
    $PropertyNames )
    BEGIN {}
    PROCESS {
    $_ | Select-Object $PropertyNames | ForEach-Object
    { # Before passing the objects on, let's set their type name so that they are all the same type all the time
    $_.PSTypeNames.Clear()
    $_.PSTypeNames.Add('InventoryRecord') # You can use whatever name you want to here.
    $_ } #End Process
    }} #endProcess #endFunction

    by poshoholic at 2012-09-30 18:14:36

    The pipeline you're trying to build isn't set up properly. The first stage emits objects with various properties. The next stage accepts those objects and determines the unique property set across all objects and passes that along. The next stage expects to receive the objects from the first stage, but that is no longer possible, because the middle-man (Select-PropertyNames) is passing it a list of property names, not objects that you want to use to select property names on.

    I recommend creating a ConvertTo-InventoryRecord command instead that does both the property name identification and the custom object creation, like this:
    function ConvertTo-InventoryRecord {
    [CmdletBinding()]
    param(
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [ValidateNotNullOrEmpty()]
    [PSObject]
    $InputObject
    )
    begin {
    $allObjects = @()
    }
    process {
    $allObjects += $InputObject
    }
    end {
    # Get all unique property names on those objects
    $propertyNames = $allObjects | ForEach-Object {$_.PSObject.Properties} | Select-Object -ExpandProperty Name -Unique

    # Now process our objects and emit custom objects with all properties
    $allObjects | Select-Object $propertyNames | ForEach-Object {
    # Before passing the objects on, let's set their type name so that they are all the same type all the time
    $_.PSTypeNames.Clear()
    $_.PSTypeNames.Add('InventoryRecord') # You can use whatever name you want to here.
    $_
    }
    }
    }

    Then you can do this:
    $service,$process | ConvertTo-InventoryRecord | Out-GridView

    by Brett.Osiewicz at 2012-09-30 18:50:49

    Yep. I understand it now. Thanks again.

You must be logged in to reply to this topic.