Merge 2 PowerShell variables - powershell

I have 2 PowerShell variables that include data in the following format. For the first table:
UserName Department
-------- ----------
X#Y.com IT
and data in the following format in the second table:
Country MobileNumber
------- ------------
Singapore +65 8xxxxxxx
and the other variable is the same with different column names and different content. I want to merge the 2 variables to have them in one single variable in that would be in the following format:
UserName Department Country MobileNumber
-------- ---------- ------- ------------
Update:
The result of Ansgar commnet, generate it in the following format:
UserName Department Country MobileNumber
-------- ---------- ------- ------------
{x#y.com, z#y.com} {IT, Sales} {Singapore, Singapore} the same here

Assuming that you have individual objects in your two variables you could construct new objects from them like this:
$obj = New-Object -Type PSObject -Property #{
'UserName' = $obj1.UserName
'Department' = $obj1.Department
'Country' = $obj2.Country
'MobileNumber' = $obj2.MobileNumber
}
If you have arrays of objects (which, judging from your updated question, is the case) you need to build the objects in a loop. Note that this assumes an equal number of objects in both variables. Note also that you MUST ensure that both arrays are in the correct order, unless you have some criteria by which you can match objects from one array to the corresponding object from the other array.
$obj = for ($i=0; $i -lt $obj1.Count; $i++) {
New-Object -Type PSObject -Property #{
'UserName' = $obj1[$i].UserName
'Department' = $obj1[$i].Department
'Country' = $obj2[$i].Country
'MobileNumber' = $obj2[$i].MobileNumber
}
}

I would create a new object and map the properties in a hashtable like this:
First, you have your variables with the data, in this example it is
$Variable1
$Variable2
Then we want to make a Hashtable, this will map the data in each of these two variables to a property in our new object, so we map the two properties Username and Department from the first variable and the Country and the MobileNumber from the second variable.
$properties = #{
'Username'=$Variable1.UserName;
'Department'=$Variable1.Department;
'Country'=$Variable2.Country;
'MobileNumber'=$Variable2.MobileNumber;}
Lastly we create a new object, in this example I call the object $User. We tell this object to contain the properties for our Hashtable:
$User = New-Object -TypeName psobject -Property $properties

Related

Combine API results into single object in Powershell

I have two API endpoints to hit and retrieve user info and write the data into a SQL table. The endpoint uses employee ID in the URL so I am looping through each user to grab their data. Endpoint #2 contains "custom fields" for the user. I am trying to combine the returns of both endpoints and write them as a single row for each user in the SQL table.
They return PSCustomObject as a hash table.
Endpoint # 1: cat6 NoteProperty string cat6=NONE
Endpoint #2: CustomText94 NoteProperty System.Management.Automation.PSCustomObject CustomText94=#{description=; value=}
function Export-Feed {
Begin {
$serverInstance = ""
$database = ""
$tableName = ""
$employees = Get-Directory | Where-Object eestatus -eq A
}
Process {
$result = $employees | ForEach-Object -Parallel {
$params = #(
'firstname',
'middlename',
'lastname',
#{n='ADID';e={(Connect-Api -apiEndpoint "/api/v1/employee/$($_.eecode)/customfield").CustomText04.value}},
'hire_date',
'rehire_date',
'position_title',
'termination_date',
'work_email',
'employee_code',
'clocksequencenumber',
'department_code',
'department_description',
'employee_status',
'supervisor_primary',
'supervisor_primary_code',
'supervisor_secondary',
'supervisor_secondary_code',
'supervisor_tertiary',
'supervisor_tertiary_code',
'supervisor_quaternary',
'cat1',
'cat1desc',
'cat2',
'cat2desc',
'cat3',
'cat3desc',
'cat4',
'cat4desc',
'cat5',
'cat5desc',
'cat6',
'cat6desc',
'cat7',
'cat7desc',
'cat8',
'cat8desc',
'cat9',
'cat9desc'
)
Connect-Api -apiEndpoint "/api/v1/employee/$($_.eecode)" | Select-Object $params
}
}
End {
$result | Out-File c:\temp\test.txt
#Write-SqlTableData -DatabaseName $database -TableName $tableName -ServerInstance $serverInstance -SchemaName dbo -InputData $result -force
}
}
To merge two custom objects ([pscustomobject] instances, which are not the same as hashtables), use the following technique:
# Two sample input objects.
$o1 = [pscustomobject] #{ one = 1; two = 2; three = 3 }
$o2 = [pscustomobject] #{ four = 4; five = 5; six = 6 }
# Merge the two, by appending the properties of the 2nd to the 1st.
# Note: This assumes that there is no overlap between the property names.
foreach ($prop in $o2.psobject.Properties) {
$o1.psobject.Properties.Add($prop, $true)
}
# $o1 now contains the union of both property sets;
# Output it.
$o1 | Format-Table
The above yields:
one two three four five six
--- --- ----- ---- ---- ---
1 2 3 4 5 6
The above relies on every object in PowerShell having a hidden .psobject property that provides reflection information about the object, notably the collection of all properties returned by the .Properties property.
PowerShell allows properties to be dynamically added to any property, which is feature of its ETS (Extended Type System).
[pscustomobject] instances contain only such dynamic properties.
The .Add() method allows adding a copy of another object's property to an object; the $true indicates that no validation need be performed, which speeds up the operation.

Powershell convert Array of Objects into PSCustomObject

I would like to convert this array of objects
Name CIDR
---- ----
sdc-MO 10.92.18.136/20
sdc-RM 10.77.6.34/20
into a single [PSCustomObject]
sdc-MO sdc-RM
------- -------
{10.92.18.136/20} {10.77.6.34/20}
Please suggest any easy way.. Thanks
Add each object to a hashtable or other dictionary dictionary type, then use the dictionary to create the object (each entry will become a separate property):
$array = #(
[pscustomobject]#{ Name = 'sdc-MO'; CIDR = '10.92.18.136/20' }
[pscustomobject]#{ Name = 'sdc-RM'; CIDR = '10.77.6.34/20' }
)
# Prepare a new dictionary to hold the properties
$newProperties = [ordered]#{}
foreach($inputObject in $array){
# If we don't already have a property with the given name,
# create a new entry in the dictionary
if(-not $newProperties.Contains($inputObject.Name)){
$newProperties.Add($inputObject.Name, #())
}
# Add the `CIDR` value to the corresponding property name
$newProperties[$inputObject.Name] += $inputObject.CIDR
}
$newObject = [pscustomobject]$newProperties
$newObject will be like what you described in the question:
PS C:\> $newObject
sdc-MO sdc-RM
------ ------
{10.92.18.136/20} {10.77.6.34/20}

Don't quite understand powershells "-Join" and "ConvertTo-Csv"

I have this code to create a csv and display it as a table:
$Keys = ("OrderDate","Region","Rep","Product","Units","Unit Cost","Total")
$Csv = #()
$Keys | ForEach-Object {
$Csv += $_ -Join ","
}
$Csv | ConvertFrom-Csv
My output is:
OrderDate
OrderDate,Region
OrderDate,Region,Rep
OrderDate,Region,Rep,Product
OrderDate,Region,Rep,Product,Units
OrderDate,Region,Rep,Product,Units,Unit Cost
OrderDate,Region,Rep,Product,Units,Unit Cost,Total
OrderDate
---------
Region
Rep
Product
Units
Unit Cost
Total
I would like these lines to be formatted as headers of a table, like this:
OrderDate Region Rep ProductUnits Unit Cost Total
----------- -------- ---- --------------- ----------- ------
What you're attempting can be done like this:
$Keys = "OrderDate","Region","Rep","Product","Units","Unit Cost","Total"
$Object = Select-Object -InputObject 0 -Property $Keys
That's typically most useful when you don't know what the properties are when writing the script. The input object doesn't matter as long as it's a scalar value (i.e., not a collection).
To get the exact output you've specified, you'd have to do $Object | Format-Table because PowerShell defaults to list output for custom objects, but that changes the object into a string so it's just for display.
A more generally useful pattern to create an object when you do know the properties is to do this:
$Object = [PSCustomObject]#{
"OrderDate" = # Some Value
"Region" = # Some Value
"Rep" = # Some Value
"Product" = # Some Value
"Units" = # Some Value
"Unit Cost" = # Some Value
"Total" = # Some Value
}
Here you instance the object and assign the values you want immediately.

Adding members to PSCustomObject variable - understanding what's going on

I am trying to get my head around what is going on here, and I was wondering if anyone knew of a resource that might point me in the right direction, or could explain it a bit for me.
I am trying to create a PSCustomObject variable, and then members to it, like this:
$myObject += [PSCustomObject]#{
FirstName = 'Bill'
LastName = 'Bobbins'
Age = '30'
}
$myObject += [PSCustomObject]#{
FirstName = 'Ben'
LastName = 'Bobbins'
Age = '40'
}
So the first bit of code executes fine, but the second bit results in an error "Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'." Also, if I pipe $myObject to get-member, I can see that $myObject is TypeName: System.Management.Automation.PSCustomObject.
Now, if I set $myObject to be an empty array first, and then try to add members, I am successful. This code works without error:
$myObject=#()
$myObject += [PSCustomObject]#{
FirstName = 'Bill'
LastName = 'Bobbins'
Age = '30'
}
$myObject += [PSCustomObject]#{
FirstName = 'Ben'
LastName = 'Bobbins'
Age = '40'
}
If I now pipe $myObject to get-member, I still get TypeName: System.Management.Automation.PSCustomObject. So my question is, why am I allowed to add multiple members to $myObject in the second example, but not in the first, when the data type is the same for both examples?
Any help is muchly appreciated!
Thanks
The issue here is with how Get-Member/the pipeline works - it's tripped me up before!
This will unroll the array, and give you the type of each element as it passes it across the pipeline:
$myObject | Get-Member
This will pass the whole object, and correctly give you the type as System.Object[]
Get-Member -InputObject $myObject
You can test this out by for example adding $myObject += "test string" to the end of your code and trying to get members both ways. The first will return both PSObject and String types.
Sidepoint: The $myObject = #() line can be avoided by specifying you are creating an array the first time you declare $myObject. Example:
[array]$myObject = [PSCustomObject]#{
[PSCustomObject[]]$myObject = [PSCustomObject]#{

Difference between PSObject, Hashtable, and PSCustomObject

Can anybody explain the details? If I create an object using
$var = [PSObject]#{a=1;b=2;c=3}
and then I look for its type using getType() PowerShell tells me it's of type Hashtable.
When using Get-Member (alias gm) to inspect the object it's obvious that a hashtable has been created, since it has a keys and a values property. So what's the difference to a "normal" hashtable?
Also, what's the advantage of using a PSCustomObject? When creating one using something like this
$var = [PSCustomObject]#{a=1;b=2;c=3}
the only visible difference to me is the different datatype of PSCustomObject. Also instead of keys and value properties, a inspection with gm shows that now every key has been added as a NoteProperty object.
But what advantages do I have? I'm able to access my values by using its keys, just like in the hashtable. I can store more than simple key-value pairs (key-object pairs for example) in the PSCustomObject, JUST as in the hashtable. So what's the advantage? Are there any important differences?
One scenario where [PSCustomObject] is used instead of HashTable is when you need a collection of them. The following is to illustrate the difference in how they are handled:
$Hash = 1..10 | %{ #{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Custom = 1..10 | %{[PSCustomObject] #{Name="Object $_" ; Index=$_ ; Squared = $_*$_} }
$Hash | Format-Table -AutoSize
$Custom | Format-Table -AutoSize
$Hash | Export-Csv .\Hash.csv -NoTypeInformation
$Custom | Export-Csv .\CustomObject.csv -NoTypeInformation
Format-Table will result in the following for $Hash:
Name Value
---- -----
Name Object 1
Squared 1
Index 1
Name Object 2
Squared 4
Index 2
Name Object 3
Squared 9
...
And the following for $CustomObject:
Name Index Squared
---- ----- -------
Object 1 1 1
Object 2 2 4
Object 3 3 9
Object 4 4 16
Object 5 5 25
...
The same thing happens with Export-Csv, thus the reason to use [PSCustomObject] instead of just plain HashTable.
Say I want to create a folder. If I use a PSObject you can tell it is wrong by
looking at it
PS > [PSObject] #{Path='foo'; Type='directory'}
Name Value
---- -----
Path foo
Type directory
However the PSCustomObject looks correct
PS > [PSCustomObject] #{Path='foo'; Type='directory'}
Path Type
---- ----
foo directory
I can then pipe the object
[PSCustomObject] #{Path='foo'; Type='directory'} | New-Item
From the PSObject documentation:
Wraps an object providing alternate views of the available members and ways to extend them. Members can be methods, properties, parameterized properties, etc.
In other words, a PSObject is an object that you can add methods and properties to after you've created it.
From the "About Hash Tables" documentation:
A hash table, also known as a dictionary or associative array, is a compact data structure that stores one or more key/value pairs.
...
Hash tables are frequently used because they are very efficient for finding and retrieving data.
You can use a PSObject like a Hashtable because PowerShell allows you to add properties to PSObjects, but you shouldn't do this because you'll lose access to Hashtable specific functionality, such as the Keys and Values properties. Also, there may be performance costs and additional memory usage.
The PowerShell documentation has the following information about PSCustomObject:
Serves as a placeholder BaseObject when PSObject's constructor with no parameters is used.
This was unclear to me, but a post on a PowerShell forum from the co-author of a number of PowerShell books seems more clear:
[PSCustomObject] is a type accelerator. It constructs a PSObject, but does so in a way that results in hash table keys becoming properties. PSCustomObject isn't an object type per se – it's a process shortcut. ... PSCustomObject is a placeholder that's used when PSObject is called with no constructor parameters.
Regarding your code, #{a=1;b=2;c=3} is a Hashtable. [PSObject]#{a=1;b=2;c=3} doesn't convert the Hashtable to a PSObject or generate an error. The object remains a Hashtable. However, [PSCustomObject]#{a=1;b=2;c=3} converts the Hashtable into a PSObject. I wasn't able to find documentation stating why this happens.
If you want to convert a Hashtable into an object in order to use its keys as property names you can use one of the following lines of code:
[PSCustomObject]#{a=1;b=2;c=3}
# OR
New-Object PSObject -Property #{a=1;b=2;c=3}
# NOTE: Both have the type PSCustomObject
If you want to convert a number of Hashtables into an object where their keys are property names you can use the following code:
#{name='a';num=1},#{name='b';num=2} |
% { [PSCustomObject]$_ }
# OR
#{name='a';num=1},#{name='b';num=2} |
% { New-Object PSObject -Property $_ }
<#
Outputs:
name num
---- ---
a 1
b 2
#>
Finding documentation regarding NoteProperty was difficult. In the Add-Member documentation, there isn't any -MemberType that makes sense for adding object properties other than NoteProperty. The Windows PowerShell Cookbook (3rd Edition) defined the Noteproperty Membertype as:
A property defined by the initial value you provide
Lee, H. (2013). Windows PowerShell Cookbook. O'Reilly Media, Inc. p. 895.
One advantage I think for PSObject is that you can create custom methods with it.
For example,
$o = New-Object PSObject -Property #{
"value"=9
}
Add-Member -MemberType ScriptMethod -Name "Sqrt" -Value {
echo "the square root of $($this.value) is $([Math]::Round([Math]::Sqrt($this.value),2))"
} -inputObject $o
$o.Sqrt()
You can use this to control the sorting order of the PSObject properties (see PSObject sorting)
I think the biggest difference you'll see is the performance. Have a look at this blog post:
Combining Objects Efficiently – Use a Hash Table to Index a Collection of Objects
The author ran the following code:
$numberofobjects = 1000
$objects = (0..$numberofobjects) |% {
New-Object psobject -Property #{'Name'="object$_";'Path'="Path$_"}
}
$lookupobjects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Path'="Path$_";'Share'="Share$_"}
}
$method1 = {
foreach ($object in $objects) {
$object | Add-Member NoteProperty -Name Share -Value ($lookupobjects | ?{$_.Path -eq $object.Path} | select -First 1 -ExpandProperty share)
}
}
Measure-Command $method1 | select totalseconds
$objects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Name'="object$_";'Path'="Path$_"}
}
$lookupobjects = (0..$numberofobjects) | % {
New-Object psobject -Property #{'Path'="Path$_";'Share'="Share$_"}
}
$method2 = {
$hash = #{}
foreach ($obj in $lookupobjects) {
$hash.($obj.Path) = $obj.share
}
foreach ($object in $objects) {
$object |Add-Member NoteProperty -Name Share -Value ($hash.($object.path)).share
}
}
Measure-Command $method2 | select totalseconds
Blog author's output:
TotalSeconds
------------
167.8825285
0.7459279
His comment regarding the code results is:
You can see the difference in speed when you put it all together. The object method takes 167 seconds on my computer while the hash table method will take under a second to build the hash table and then do the lookup.
Here are some of the other, more-subtle benefits:
Custom objects default display in PowerShell 3.0
We have a bunch of templates in our Windows-PKI and we needed a script, that has to work with all active templates. We do not need to dynamically add templates or remove them.
What for me works perfect (since it is also so "natural" to read) is the following:
$templates = #(
[PSCustomObject]#{Name = 'template1'; Oid = '1.1.1.1.1'}
[PSCustomObject]#{Name = 'template2'; Oid = '2.2.2.2.2'}
[PSCustomObject]#{Name = 'template3'; Oid = '3.3.3.3.3'}
[PSCustomObject]#{Name = 'template4'; Oid = '4.4.4.4.4'}
[PSCustomObject]#{Name = 'template5'; Oid = '5.5.5.5.5'}
)
foreach ($template in $templates)
{
Write-Output $template.Name $template.Oid
}
Type-1: $PSCustomObject = [PSCustomObject] #{a=1;b=2;c=3;d=4;e=5;f=6}
Type-2: $PsObject = New-Object -TypeName PSObject -Property #{a=1;b=2;c=3;d=4;e=5;f=6}
The only difference between Type-1 & Type-2
Type-1 Property are displayed in same order as we added
Type-1 enumerates the data faster
Type-1 will not work with systems running PSv2.0 or earlier
Both Type-1 & Type-2 are of type “System.Management.Automation.PSCustomObject”
Difference between HashTable and PSCustomObject/PSObject is
You can add new methods and properties to PSCustomObject/PSObject
You can use PSCustomObject/PSObject for pipeline parameter binding using ValueFromPipelineByPropertyName as explained by Zombo
example: [PSCustomObject] #{Path='foo'; Type='directory'} | New-Item