I am trying to find the matching names in two different types of Powershell objects
$Object1 has two properties - Name (string), ResourceID (uint32)
$object2 has one noteproperty - Name (system.string)
This gives me a list of the matching names but I also want the corresponding resourceID property from $object1.
$computers = Compare-Object $Object1.name $WSD_CM12 | where {$_.sideindicator -eq "=>"} | foreach {$_.inputobject}
These are big objects with over 10,000 items so I'm looking for the most efficient way to accomplish this.
If I'm understanding what you're after, I'd start by creating a hash table from your Object1 collection:
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
Then you can find the ResourceID for any given Object2.name with:
$object1_hash[$Object2.Name]
Test bed for creating hash table:
$object1_coll = $(
New-Object PSObject -Property #{Name = 'Name1';ResourceID = 001}
New-Object PSObject -Property #{Name = 'Name2';ResourceID = 002}
)
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
$object1_hash
Name Value
---- -----
Name2 2
Name1 1
Alternative method:
# Create sample list of objects with both Name and Serial
$obj1 = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; Serial = "1234" }
$obj2 = New-Object -Type PSCustomObject -Property:#{ Name = "Cow"; Serial = "4242" }
$collection1 = #($obj1, $obj2)
# Create subset of items with only Name
$objA = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; }
$collection2 = #($objA)
#Everything above this line is just to make sample data
# replace $collection1 and $collection2 with $Object1, $WSD_CM12
# Combine into one list
($collection1 + $collection2) |
# Group by name property
Group-Object -Property Name |
# I only want items that exist in both
Where { $_.Count -gt 1 } |
# Now give me the object
Select -Expand Group |
# And get the properties
Where { $_.Serial -ne $null }
Related
first of all sorry if my english is not the best. but ill try to explain my issue with as much detail as i can
Im having an issue where i cant get Format-Table to effect the output i give it.
below is the part im having issues with atm.
cls
$TotalSize = $($mailboxes. #{name = ”TotalItemSize (GB)”; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split(“(“)[1].Split(” “)[0].Replace(“,”, ””) / 1GB), 2) } });
$UserN = $($mailboxes.DisplayName)
$itemCount = $($mailboxes.ItemCount)
$LastLogonTime = $($mailboxes.ItemCount)
$allMailboxinfo = #(
#lager dataen som skal inn i et objekt
#{Username= $UserN; ItemCount = $itemCount; LastLogonTime = $($mailboxes.ItemCount); Size = $TotalSize}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Table = $allMailboxinfo | Format-Table | Out-String
$Table
the output of this gives me what almost looks like json syntax below each title of the table.
Username LastLogonTime ItemCount Size
-------- ------------- --------- ----
{username1, username2,username3,userna...} {$null, $null, $null, $null...} {$null, $null, $null, $null...} {$null, $null, $null, $null...}
running the commands by themselves seem to work tho. like $mailboxes.DisplayName gives the exact data i want for displayname. even in table-format.
the reason im making the table this way instead of just using select-object, is because im going to merge a few tables later. using the logic from the script below.
cls
$someData = #(
#{Name = "Bill"; email = "email#domain.com"; phone = "12345678"; id = "043546" }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$moreData = #(
#{Name = "Bill"; company = "company 04"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Merge = #(
#plots the data into a new table
#{Name = $($someData.Name); e_mail = $($someData.email); phone = $($someData.phone); id = $($someData.id); merged = $($moreData.company) }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
#formatting table
$Table = $Merge | Format-Table | Out-String
#print table
$Table
if you are wondering what im doing with this.
My goal, all in all. is a table with using the info from Exchange;
DisplayName, TotalItemSize(GB), ItemCount, LastLogonTime, E-mail adress, archive + Maxquoata, Quoata for mailbox.
You're creating a single object where each property holds an array of property values from the original array of mailbox objects.
Instead, create 1 new object per mailbox:
# construct output objects with Select-Object
$allMailBoxInfo = $mailboxes |Select #{Name='Username';Expression='DisplayName'},ItemCount,#{Name='LastLogonTime';Expression='ItemCount'},#{Name='Size';Expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) }}
# format table
$Table = $allMailBoxInfo | Format-Table | Out-String
# print table
$Table
I created the following object:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$ClusterNodeSSDs = New-Object PSObject -Property $PSOhash
and want to add values from the following command into it:
$SSDModel = "xyz123"
$ClusterNode = "Node1"
gwmi -Namespace root\wmi ClusPortDeviceInformation| select ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId | sort ConnectedNodeDeviceNumber | where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
which returns the proper informations, but need them as properties in the object for further processing.
If you want to add a set of property-value pairs to an already existing PSObject ($MyObject in this example) that currently does not have those properties, you can use the Add-Member command for this:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$MyObject = $MyObject | Add-Member -NotePropertyMembers $PSOHash
Explanation:
The -NotePropertyMembers parameter allows you do add a hash table of property-value pairs to a custom object.
Optionally, you can use a combination of Add-Member's -NotePropertyValue and -NotePropertyName to add properties one at a time.
If you want to update one object's property values with property values (same property names) from another object, you can just use direct assignment and the member access operator (.).
$SSDModel = "xyz123"
$ClusterNode = "Node1"
$WmiObjects = Get-WmiObject -Namespace root\wmi ClusPortDeviceInformation |
Select-Object ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId |
Sort-Object ConnectedNodeDeviceNumber |
where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
$ClusterNodeSSDs = foreach ($WmiObject in $WmiObjects) {
$PSOhash = #{
ConnectedNode = $WmiObject.ConnectedNode
ConnectedNodeDeviceNumber = $WmiObject.ConnectedNodeDeviceNumber
Serialnumber = $WmiObject.Serialnumber
ProductId = $WmiObject.ProductId
}
[pscustomobject]$PSOhash
}
Explanation:
Note the use of the foreach loop here because the Get-WmiObject will likely return a collection. So you will need to iterate all of them to create custom objects. However, it just seems that you can just use the Get-WmiObject | Select-Object output to perform the same thing.
If I have a list of objects
$o1 = New-Object –TypeName PSObject –Property #{"MyKey"= "dog"; "MyValue"="Steve"}
$o2 = New-Object –TypeName PSObject –Property #{"MyKey"= "dog"; "MyValue"="Frank"}
$o3 = New-Object –TypeName PSObject –Property #{"MyKey"= "fish"; "MyValue"="Frank"}
$inputs = ($o1, $o2, $o3)
And a function that groups them into a table and then displays them
Function Report
{
param($records)
$hashtable = $records | Group-object -AsHashTable -Property "MyKey"
Write-host "Format table:"
$hashtable | format-table
Write-host "Contains key:"
$hashtable.Keys | %{ "Key '$_' found: " + $hashtable.ContainsKey($_) }
}
So when I run this over my input list
Report($inputs)
I get
Name Value
---- -----
dog {#{MyKey=dog; MyValue=Steve}, #{MyKey=dog; MyValue=Frank}}
fish {#{MyKey=fish; MyValue=Frank}}
Contains key:
Key 'dog' found: True
Key 'fish' found: True
As I would expect.
I can now run my list through select-object
Report($inputs | select-object -Property MyKey, MyValue )
and get the same output from Report as above.
However if I use an expression for the MyKey value:
Report( $inputs | select-object -Property `
#{ Label = "MyKey"; Expression = {$_.MyKey} },
MyValue )
I get a grouped table that looks the same, but doesn't allow me to access values by key:
Name Value
---- -----
dog {#{MyKey=dog; MyValue=Steve}, #{MyKey=dog; MyValue=Frank}}
fish {#{MyKey=fish; MyValue=Frank}}
Contains key:
Key 'dog' found: False
Key 'fish' found: False
Why can't I access this hashtable by key?
Note that this question looks a lot like this one but that solution did not work here. That is:
Add-Type -TypeDefinition #'
public static class Helper {
public static object IndexHashtableByPSObject(System.Collections.IDictionary table,object[] key) {
return table[key[0]];
}
}
'#
Function Report
{
param($records)
$hashtable = $records | Group-object -AsHashTable -Property "MyKey"
$hashtable.Keys | %{ "Get using helper:" + [Helper]::IndexHashtableByPSObject($hashtable,$_) }
$hashtable.Keys | %{ "Get using reflection:" + [Collections.IDictionary].InvokeMember('','GetProperty',$null,$hashtable,$_) }
}
returns
Get using helper:
Get using helper:
Get using reflection:
Get using reflection:
am I implementing those solutions wrong?
I'm trying to display some data my script generates in a PSObject, so I can then export to a CSV, but the only object that shows is whichever one I add to the array first.
$pass=#("1","2","3")
$fail=#("4")
$obj=#()
$pass | % {
$obj+=New-Object PSObject -Property #{Pass=$_}
}
$fail | % {
$obj+=New-Object PSObject -Property #{Fail=$_}
}
$obj
I've also tried this, but I get a blank line showing in the table where the value isn't in that column, which I don't want:
$pass=#("1","2","3")
$fail=#("4")
$obj=#()
$pass | % {
$obj+=New-Object PSObject -Property #{Pass=$_;Fail=""}
}
$fail | % {
$obj+=New-Object PSObject -Property #{Pass="";Fail=$_}
}
$obj
My desired result:
Pass Fail
---- ----
1 4
2
3
I am using Powershell V2.
Another answer is right - you're using objects wrong. That being said, here's a function to help you use them wrong!
Function New-BadObjectfromArray($array1,$array2,$array1name,$array2name){
if ($array1.count -ge $array2.count){$iteratorCount = $array1.count}
else {$iteratorCount = $array2.count}
$obj = #()
$iteration=0
while ($iteration -le $iteratorCount){
New-Object PSObject -Property #{
$array1name=$array1[$iteration]
$array2name=$array2[$iteration]
}
$iteration += 1
}
$obj
}
$pass=#("1","2","3")
$fail=#("4")
New-BadObjectfromArray -array1 $fail -array2 $pass -array1name "Fail" -array2name "Pass"
As you figured out yourself, PowerShell only outputs the properties of the first item in your array. Its not designed to print the ouput you are expecting in the way you are using it.
As a workaround, you could use a for loop to "build" your desired output:
$pass=#("1","2","3")
$fail=#("4")
$obj=#()
for ($i = 0; $i -lt $pass.Count; $i++)
{
if ($fail.Count -gt $i)
{
$currentFail = $fail[$i]
}
else
{
$currentFail = ""
}
$obj+=New-Object PSObject -Property #{Fail=$currentFail;Pass=$pass[$i];}
}
$obj | select Pass, Fail
Output:
Pass Fail
---- ----
1 4
2
3
I'm writing some reporting scripts in Powershell and collecting up a summary table of items as a blank object with additional properties added one by one:
$cmClusters = #()
foreach ($Cluster in Clusters) {
$cmCluster = New-Object System.Object
$cmCluster | Add-Member -type NoteProperty -Name VC -Value $strVC
$cmCluster | Add-Member -type NoteProperty -Name Name -Value $Cluster.name
# etc...
$cmClusters += $cmCluster;
}
If I just dump $cmClusters at the end of this, I get a format-list output with the properties in the order that I added them.
However, I was hoping to write a generic "dump this collection of objects to an excel tab" function to produce my report, which will be several similar worksheet tabs from different lists of objects.
That looks like this:
function DumpToExcel($workbook, $tabTitle, $list)
{
$sheet = $workbook.worksheets.add()
$sheet.Name = $tabTitle
$col = 1
$row = 1
$fields = $list[0] | Get-Member -MemberType NoteProperty | Select-Object *
Foreach ($field in $fields) {
$sheet.cells.item($row,$col++) = $field.Name
}
$heading = $sheet.UsedRange
$heading.Font.Bold = $True
$row++
Foreach ($cmCluster in $list) {
$col=1
Foreach ($field in $fields) {
$sheet.cells.item($row,$col++) = $cmCluster.($field.Name)
}
$row++
}
$sheet.UsedRange.EntireColumn.AutoFit() | Out-Null
}
which works, but the property names are now in alphabetical order.
What can I use to get my list of properties in the same order that Format-List does?
Try this:
$fields = $list[0].psobject.properties | select name