Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Join Object

Posted on May 14, 2010March 15, 2014

Related to some of the WMI stuff I’ve been working on lately is the idea of melding or joining objects. This comes about because I often see forum posts from administrators looking to collect information from different WMI classes but present it as a single object.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

One way you might accomplish this is to create a new, custom object with the New-Object cmdlet.

#requires -version 2.0

Function Join-Object {

<#
.Synopsis
    Join multiple objects together into a single object.
.Description
    This function is designed to join, or merge two or more objects together. You can either pipe 
    objects via cmdlets to Join-Object or specify a comma separated list of objects. A new object
    will be created with all unique properties and values from the original objects. If a property
    is duplicated among objects but with a different value, then it will be renamed. For example,
    if two objects both have a Name property but with different values, the custom object will have
    properties of Name and Name_1.
    
    Join-Object works best with input objects that themselves are not arrays or collections of other
    objects, but it can be done. The intent was to be able to merge related information into a single
    object that you could then export or convert.
    
    If you are joining WMI objects, you might prefer to use the Select-WMI function to strip out system
    properties like __CLASS. You can download this function from https://jdhitsolutions.com/blog/2010/05/select-wmi/.
    
    By default the new object will contain a property called _TypeInformation which will store an array
    of type information from the original objects. Use -NoTypeInformation to skip this.
    
    You can also include the original source objects in their "raw" state as part of the new object. 
    Specify -IncludeOriginal to store the input objects as an array in the _SourceObject property.
.Parameter InputObject
    Any valid PowerShell object. 
.Parameter IncludeOriginal
    If specified, the original input object will be stored in an array
    and be defined in the _SourceObject property on the new, custom object.
.Parameter NoTypeInformation
    By default, each input object's type is stored in an array and defined
    in the _TypeInformation property on the new, custom object. If you do
    not want this information, then specify this switch.
.Example
    PS C:\> (gwmi win32_computersystem),(gwmi win32_bios) | join-object

    Combine two WMI objects into a single object.
.Example
    PS C:\> (gwmi win32_computersystem),(gwmi win32_bios) | select-wmi | join-object -NoTypeInformation
    
    Combine two WMI objects into a single object, but without the class properties. Select-WMI is a
    third-party developed function you can download from https://jdhitsolutions.com/blog/2010/05/select-wmi/
.Example
    PS C:\> $computers | foreach { (gwmi win32_operatingsystem -ComputerName $_),(gwmi win32_computersystem -ComputerName$_) | select-wmi | join-object} | export-csv c:\temp\sysreport.csv -NoTypeInformation
    
    Where $computers is a collection of computernames, get WMI information from two classes 
    (win32_operatingsystem and win32_computersystem). Join these two objects, stripping out 
    system properties and export the results to a csv file. Each computer will have it's own
    custom object comprised of both WMI classes.
.Example
    PS C:\> $data=$fso.GetFile("c:\pagefile.sys"),$fso.GetDrive("c:") | join-object -IncludeOriginal
    
    $fso is a COM FileSystemObject. The results of the two methods are joined and safved to $data. You can
    view the original objects by looking at $data._SourceObjects
.Inputs
   Any valid object
.Outputs
    A custom object

.Link
    New-Object
.Link
https://jdhitsolutions.com/blog/2010/05/join-object/

.Notes
     NAME:      Join-Object
     VERSION:   1.0
     AUTHOR:    Jeffery Hicks (@JeffHicks)
     LASTEDIT:  May 12, 2010

     Learn more:
       PowerShell in Depth: An Administrator's Guide
       PowerShell Deep Dives 
       Learn PowerShell 3 in a Month of Lunches 
       Learn PowerShell Toolmaking in a Month of Lunches 

     "Those who forget to script are doomed to repeat their work."

      ****************************************************************
      * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
      * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
      * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
      * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
      ****************************************************************


#>
   
[CmdletBinding()]
Param(
    [Parameter(Position=0,
               ValueFromPipeline=$True,
               Mandatory=$True,
               HelpMessage="Enter a valid PowerShell object")]
    [object[]]$Inputobject,
    [switch]$IncludeOriginal,
    [switch]$NoTypeInformation
)  
Begin {
    write-verbose "Starting $($myinvocation.mycommand)"
    #initialize a hash table for new properties
    $newProp=@{}
    #initialize a hash table to handle duplicate property names
    $duplicate=@{}
    #initialize an array to hold raw objects
    $raw=@()
    #initialize an array to hold object metadata
    $metatypes=@()
}

Process {
  foreach ($parentobject in $InputObject) {
  #handle inputobjects that are arrays themselves
    foreach ($object in $parentobject) {
    $raw+=$object
    write-verbose "Adding type $($object.GetType().Fullname)"
    $metaTypes+=$object.GetType().Fullname
      $object | get-member -MemberType Properties | foreach {
       
        if ($newProp.Contains($_.name) ) {
            write-Verbose "Property $($_.name) already defined $($newProp.item($_.name))"
            #skip properties that have duplicate values
            #only include the property if it has a different value
            #and then rename the property like myProperty_1
            if ($newProp.item($_.name) -ne $object.($_.Name)) { 
                $duplicate.item($_.name)+=1
                $newName=$_.name+"_"+$duplicate.item($_.name)
                write-Verbose "Adding $newName =  $($object.($_.Name))."
                $newProp.Add($newName,$object.($_.name))
            }
        }
        else {
            Write-Verbose "Adding property $($_.name)"
            $newProp.Add($_.name,$object.($_.name))
        }
      } #foreach
     } #foreach object
  } #foreach parentobject

} #Process

End {
    if ($IncludeOriginal) {
        #add raw objects as a property
        Write-Verbose "Adding original raw objects"
        $newProp.Add("_SourceObjects",$raw)
    }
    
    if ($NoTypeInformation) {
        write-verbose "Skipping meta type information"
    }
    else {
        #add unique object metadata as a property
        $newProp.Add("_TypeInformation",($metaTypes | Select-object -unique))
    }
    
    #write the new object to the pipeline
    New-Object PSObject -Property $NewProp
    write-verbose "Ending $($myinvocation.mycommand)"
}
    
} #end Function

#this script also defines an alias for this function.
Set-Alias -Name jo -Value Join-Object 

Join-Object will get the properties for each object and copy them to a new object. If a property name is duplicated with duplicate values, it is only added once. But if two objects have the same property but different values, then each subsequent property is renamed in incremented, eg Name, Name_1, Name_2.

If using WMI objects, I suggest also using my Select-WMI function to strip out the system classes.

Join-Object works best with objects that are not collections of objects themselves. It will work, but gets a little tricky to sort out.

I also include metadata information in the new object based on the input objects. Each input object type is stored as an array in the _TypeInformation property. You can skip this by using the –NoTypeInformation parameter. Similarly, you can elect to include the “raw”, input objects with –IncludeOriginal. The new object will have a _SourceObjects property.

As always, I hope you’ll let me know what you think.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d