7

I have 2 objects that I will like to merge, but it seems I cannot find the solution

Messages                                   Name                                                                           Error
--------                                   ----                                                                           -----
{\\APPS-EUAUTO1\C$\Users\xautosqa\AppDa... test 1                                                                          True
{[APPS-EUAUTO1] [prep] Setting agent op... test 2                                                                         False

TestPlan        Script          TestCase        TestData        ErrorCount      ErrorText       DateTime        Elapsed        
--------        ------          --------        --------        ----------      ---------       --------        -------        
D:\XHostMach... D:\XHostMach... rt1             1,\a\""         1               [#ERROR#][AP... 2014-03-28 1... 0:00:18        
D:\XHostMach... D:\XHostMach... rt2             1,\a\""         0                               2014-03-28 1... 0:00:08      

I have tried :

 function Join-Object {
    Param(
       [Parameter(Position=0)]
       $First
    ,
       [Parameter(Position=1,ValueFromPipeline=$true)]
       $Second
    )
    BEGIN {
       [string[]] $p1 = $First | gm -type Properties | select -expand Name
    }
    Process {
       $Output = $First | Select $p1
       foreach($p in $Second | gm -type Properties | Where { $p1 -notcontains $_.Name } | select -expand Name) {
          Add-Member -in $Output -type NoteProperty -name $p -value $Second."$p"
       }
       $Output
    }
 } # End: Function Join-Object

$TestCases = Join-Object $TxtTestcases $RexTestcases | Select Name, TestPlan, TestCase, Script, TestData, Error, ErrorCount, ErrorText, Messages, DateTime, Elapsed

but the second object is not present :

Name         TestPlan     TestCase     Script       TestData            Error ErrorCount   ErrorText    Messages    DateTime   
----         --------     --------     ------       --------            ----- ----------   ---------    --------    --------   
test 1                                                                   True                           {\\APPS-...            
test 2                                                                  False                           {[APPS-E...            

I have also tried :

Function Merge-Testcase {
Param ($TxtTestcase, $RexTestcase)
    $Fields = @{            
        Name        = $TxtTestcase.Name
        TestPlan    = $RexTestcase.TestPlan
        TestCase    = $RexTestcase.TestCase
        Script      = $RexTestcase.Script
        TestData    = $RexTestcase.TestData
        IsError     = $TxtTestcase.Error
        ErrourCount = $RexTestcase.ErrorCount
        ErrorText   = $RexTestcase.ErrorText
        Messages    = $TxtTestcase.Messages
        DateTime    = $RexTestcase.DateTime
        Elapsed     = $RexTestcase.Elapsed
    }
    New-Object PSObject -Property $Fields | ConvertTo-Csv -NoTypeInformation
} # End: Function Merge-Testcase
$TestCases = Merge-Testcase -TxtTestcase $TxtTestcases -RexTestcas $RexTestcase
$TestCases | Format-Table

but I am getting this

"Script","Name","TestCase","TestPlan","ErrorText","TestData","ErrourCount","IsError","Elapsed","DateTime","Messages"
,,,,,,,,,,

Any idea on how to join the 2 objects ?

user2066657
  • 444
  • 1
  • 4
  • 23
Ionut
  • 1,729
  • 4
  • 23
  • 50
  • Good question - I posted an answer with two potential solutions. I think you will like the second one :) –  Apr 01 '14 at 20:00
  • I could swear I answered a question just like this one like 4 or 5 days ago. Actually, my answer looked extremely similar to Trevor's second solution for you. Did you try looking for a solution in questions that were already answered here on SO? – TheMadTechnician Apr 01 '14 at 20:38
  • @TheMadTechnician Yes, I have tried a few things but nothing worked and I think because I had arrays – Ionut Apr 03 '14 at 16:01
  • Using this [`Join-Object`](https://www.powershellgallery.com/packages/Join): `$Obj1 | Join $Obj2` – iRon Feb 24 '19 at 19:32

4 Answers4

11

Option 1

One option would be to put both object into a HashTable, and then cast it as a PSCustomObject.

 $Obj1 = (Get-Process)[0]; # Get a process
 $Obj2 = (Get-Service)[0]; # Get a service
 ($Result = [PSCustomObject]@{ Obj1 = $Obj1; Obj2 = $Obj2; });

 $Result.Obj1.Name;
 $Result.Obj2.Name;

NOTE: This will create an additional "level" that you have to drill into, so it's not ideal, but it will work.

Option #2

The second option would be to iterate over all the properties of the "second object" and add them to the "first object" using Add-Member, which your example already shows.

Create an empty file called c:\test\test.txt, and then run the following code:

# Get a couple of objects (with different property names)
$Object1 = Get-Service -Name Dnscache;
$Object2 = Get-Item c:\test\test.txt;

# Get a list of the properties on both objects
$PropertyList1 = @(Get-Member -InputObject $Object1 -MemberType Properties).Name;
$PropertyList2 = Get-Member -InputObject $Object2 -MemberType Properties | Where-Object -FilterScript { $PropertyList1 -notcontains $PSItem.Name; };

# Add the properties, from the second object, to the first object
foreach ($Property in $PropertyList2) {
    Write-Host ('Adding property: {0}' -f $Property.Name);
    Add-Member -InputObject $Object1 -Name $Property.Name -MemberType NoteProperty -Value $Object2.$($Property.Name);
}

# Output the object
$Object1 | select *;

The output looks like the following:

Mode                : -a---
PSChildName         : test.txt
PSDrive             : C
PSIsContainer       : False
PSParentPath        : Microsoft.PowerShell.Core\FileSystem::C:\test
PSPath              : Microsoft.PowerShell.Core\FileSystem::C:\test\test.txt
PSProvider          : Microsoft.PowerShell.Core\FileSystem
Attributes          : Archive
CreationTime        : 3/11/2014 3:06:43 PM
CreationTimeUtc     : 3/11/2014 8:06:43 PM
Directory           : C:\test
DirectoryName       : C:\test
Exists              : True
Extension           : .txt
FullName            : C:\test\test.txt
IsReadOnly          : False
LastAccessTime      : 3/11/2014 3:06:43 PM
LastAccessTimeUtc   : 3/11/2014 8:06:43 PM
LastWriteTime       : 3/11/2014 3:06:29 PM
LastWriteTimeUtc    : 3/11/2014 8:06:29 PM
Length              : 0
BaseName            : test
VersionInfo         : File:             C:\test\test.txt
                      InternalName:     
                      OriginalFilename: 
                      FileVersion:      
                      FileDescription:  
                      Product:          
                      ProductVersion:   
                      Debug:            False
                      Patched:          False
                      PreRelease:       False
                      PrivateBuild:     False
                      SpecialBuild:     False
                      Language:         

Name                : Dnscache
RequiredServices    : {nsi, Tdx}
CanPauseAndContinue : False
CanShutdown         : False
CanStop             : True
DisplayName         : DNS Client
DependentServices   : {NcaSvc}
MachineName         : .
ServiceName         : Dnscache
ServicesDependedOn  : {nsi, Tdx}
ServiceHandle       : 
Status              : Running
ServiceType         : Win32ShareProcess
Site                : 
Container           : 

See how the properties from the service AND the file are both on the one object?

  • Will that grab all the various "Property" types (for example NoteProperty which a lot of custom objects end up with)? – TheMadTechnician Apr 01 '14 at 20:42
  • Yes, it does. I used `-MemberType Properties`, which includes `NoteProperty`. –  Apr 01 '14 at 21:23
  • Awesome! I'll have to go that route in the future, this looks cleaner than my `GM|?{$_.MemberType -Match "Property"}` that I used. Thanks Trevor! – TheMadTechnician Apr 01 '14 at 21:27
  • @TrevorSullivan when I run your code I get this error `Add-Member : Cannot bind argument to parameter 'Name' because it is null. At line:12 char:43` and I do not understand why – Ionut Apr 03 '14 at 13:16
  • @ionut are you running PowerShell version 4.0? That's what the code was written and tested on. –  Apr 03 '14 at 13:17
  • No, I am using PowerShell 2.0, maybe this is the reason :-( – Ionut Apr 03 '14 at 13:37
  • Yeah, I would recommend upgrading to .NET Framework 4.5.1 and PowerShell version 4.0. –  Apr 03 '14 at 13:49
2
# Create two Arrays partial name 
# combine the two arrays

$Object1 = @()

# Create a PSObject with the associated properties hashtable and add to $userObj
$userObj= New-Object PSObject -Property @{
  name="Decvic-srv1"
  Share='$admin'
  Path="c:\temp"
  SizeKB=[math]::Round((200456789/1KB),2)
  Files="345"
  }

$Object1 += $userObj
$Object1

$Object2 = @()

# Create a PSObject with the associated properties hashtable and add to $userObj
$userObj= New-Object PSObject -Property @{
  name="Decvic-srv2"
  Share='$test'
  Path="c:\temp\robertp"
  SizeKB=[math]::Round((4567789/1KB),2)
  Files="1000"
  Desc="Secondary file that needs to have write acess"
  Function="1ST Function"
  }

$Object2 += $userObj
$Object2
$First = $Object1
$Second = $Object2

$Object3 = @()

# Get a list of the properties on both objects
$PropertyList1 = $Object1 | Get-Member -Type Properties | select -expand Name
$PropertyList2 = $Object2 | Get-Member -Type Properties | select -expand Name | 
Where- 
Object -FilterScript { $PropertyList1 -notcontains $PSItem.Name }

$userObj = New-Object PSObject

# Add the 1st object properties, from the second object, to the first object
foreach ($Property in $PropertyList1) {
#Write-Host ('Adding property: {0}' -f $Property)
Add-Member -InputObject $userObj -Name $Property -MemberType NoteProperty -Value 
$Object1.$($Property)
}

$Object3 += $userObj

$userObj = New-Object PSObject

# Add 2nd object the properties, from the second object, to the first object
foreach ($Property in $PropertyList2) {
# Write-Host ('Adding property: {0}' -f $Property)
Add-Member -InputObject $userObj -Name $Property -MemberType NoteProperty -Value 
$Object2.$($Property)
}

$Object3 += $userObj

# Output the object
$Object3 | select Desc, Files, Function, name, Path, Share, SizeKB | ft
Community
  • 1
  • 1
Robert
  • 21
  • 1
1

The main problem in your original post appears to be that $TxtTestcases and $RexTestcases are arrays with two objects each, but your Join-Object function is not written to enumerate over arrays. Based on what I can see, your function should work fine if you were to call it like this:

Join-Object @($TxtTestcases)[0] @($RexTestcases)[0]

It's up to you how you decide to choose which objects in the array get joined, or what to do if the arrays contain different numbers of objects. There doesn't appear to be any common field that you're "joining" on here (and if there were, you could use the Join-Object function from http://blogs.msdn.com/b/powershell/archive/2012/07/13/join-object.aspx ).

Dave Wyatt
  • 1,508
  • 14
  • 9
  • Yes, I changed for this `$TestCases = @(); for($i=0; $i -lt $TxtTestcases.length;$i++) { $TestCases += Join-Object @($TxtTestcases)[$i] @($RexTestcases)[$i] };` and it worked fine, thanks – Ionut Apr 03 '14 at 13:33
1

For quiet some time I am maintaining a Join-Object cmdlet (see: In Powershell, what's the best way to join two tables into one?).
My mission for this project is to keep the syntax as simple as possible and get the best possible performance while still respecting the PowerShell development guidelines with respect to the pipeline.
For the things @Dave Wyatt points out:

  • "There doesn't appear to be any common field": in the SQL Join clause you use the ON argument for this, as with the Join-Object cmdlet but if you omit the -On parameter, a join on the object index is done.
  • "or what to do if the arrays contain different numbers of objects", similar to a usual join on related properties, you can control how to deal with different table lengths using the included proxy commands LeftJoin-Object (alias LeftJoin), RightJoin-Object (alias RightJoin) and FullJoin-Object (alias FullJoin) for this (see help).

As for this specific question:

$TxtTestcases | Join-Object $RexTestcases | Format-Table *

Messages                                   Name   Error TestPlan        Script          TestCase TestData ErrorCount ErrorText       DateTime   Elapsed
--------                                   ----   ----- --------        ------          -------- -------- ---------- ---------       --------   -------
{\\APPS-EUAUTO1\C$\Users\xautosqa\AppDa... test 1 True  D:\XHostMach... D:\XHostMach... rt1      1,\a\""  1          [#ERROR#][AP... 2014-03-28 0:00:18
{[APPS-EUAUTO1] [prep] Setting agent op... test 2 False D:\XHostMach... D:\XHostMach... rt2      1,\a\""  0                          2014-03-28 0:00:08
iRon
  • 20,463
  • 10
  • 53
  • 79