Structs or Objects in Powershell 2 - powershell

Does the latest version of Powershell have the ability to do something like JavaScript's:
var point = new Object();
point.x = 12;
point.y = 50;
If not, what is the equivalent or workaround?
UPDATE
Read all comments

The syntax is not directly supported by the functionality is there via the add-member cmdlet's. Awhile ago, I wrapped this functionality in a general purpose tuple function.
This will give you the ability to one line create these objects.
$point = New-Tuple "x",12,"y",50
Here is the code for New-Tuple
function New-Tuple()
{
param ( [object[]]$list= $(throw "Please specify the list of names and values") )
$tuple = new-object psobject
for ( $i= 0 ; $i -lt $list.Length; $i = $i+2)
{
$name = [string]($list[$i])
$value = $list[$i+1]
$tuple | add-member NoteProperty $name $value
}
return $tuple
}
Blog Post on the subject: http://blogs.msdn.com/jaredpar/archive/2007/11/29/tuples-in-powershell.aspx#comments

For simple ways, first, is a hashtable (available in V1)
$obj = #{}
$obj.x = 1
$obj.y = 2
Second, is a PSObject (easier in V2)
$obj = new-object psobject -property #{x = 1; y =2}
It gives you roughly the same object, but psobjects are nicer if you want to sort/group/format/export them

Sorry, even though the selected answer is good, I couldn't resist the hacky one line answer:
New-Object PsObject | Select-Object x,y | %{$_.x = 12; $_.y = 50; $foo = $_; }

You can do it like this:
$point = New-Object Object |
Add-Member NoteProperty x ([int] 12) -passThru |
Add-Member NoteProperty y ([int] 15) -passThru
Regarding one of your comments elsewhere, custom objects may be more useful than hash tables because they work better with cmdlets that expect objects to have named properties. For example:
$mypoints | Sort-Object y # mypoints sorted by y-value

$point = "" | Select #{Name='x'; Expression={12}} ,#{Name='y'; Expression={15}}
or more intuitively
$point = "" | Select x,y
$point.x=12; $point.y=15

Related

How would I store my search results into a table using a foreach loop in powershell?

Import-Module <JAMS>
$JAMSHistories = Get-JAMSHistory -Server TESTDUMMY2 -StartDate "01/20/2020" -EndDate "01/24/2020"
$historyTable = #()
foreach($JAMSHistory in $JAMSHistories)
{
$row = New-Object -TypeName PSObject
Write-Host $JAMSHistory.FinalSeverity
if($JAMSHistory.FinalSeverity -match 'Success')
{
$row | Add-Member -NotePropertyName JobSeveritySuccess ($JAMSHistory.FinalSeverity)
}
else {
if ($JAMSHistory.FinalSeverity -match 'Error') {
$row | Add-Member -NotePropertyName JobSeverityError ($JAMSHistory.FinalSeverity) }
} $historyTable += $row
}
$historyTable
You can do the following, which will output an array of objects ($historyTable) with a properties called JobSeveritySuccess and JobSeverityError.
$JAMSHistories = Get-JAMSHistory -Server TESTDUMMY2 -StartDate "01/20/2020" -EndDate "01/24/2020"
$historyTable = foreach ($JAMSHistory in $JAMSHistories) {
$row = "" | Select JobSeveritySuccess,JobSeverityError
if ($JAMSHistory.FinalSeverity -match 'Success') {
$row.JobSeveritySuccess = $JAMSHistory.FinalSeverity
}
elseif ($JAMSHistory.FinalSeverity -match 'Error') {
$row.JobSeverityError = $JAMSHistory.FinalSeverity
}
$row
}
# Output
$historyTable
# Output in Table Format
$historyTable | Format-Table
The problem with this approach is that every object will have JobSeveritySuccess and JobSeverityError properties, and they may be empty. The only time both properties will have data is if $JAMSHistory.FinalSeverity contains Error and Success for the same object. There is probably a better way to do your design if provided more requirements.
When doing this type of exercise, there are always some gotchas. Consider creating a new PSObject or PSCustomObject for loop iteration. Then output that object at the end of the loop code. When collecting the foreach loop output, just set a variable equal to the foreach loop. There's rarely a need to use += to build an array of foreach loop output. There could be some exceptions to this, but most of the time they are good things to consider.

Dynamically creating the name of an existing object to Set one of its properties

I have the following code in Powershell:
function New-Row-Object-Instance {
New-Object PSObject -Property #{
Zeros = 0
Tens = 0
Twentys = 0
Thirtys = 0
Fortys = 0
Fiftys = 0
Sixtys = 0
}
$Row_Details = New-Row-Object-Instance
I Updated $Row_Details with some values. Now I have the following Labels on a Windows Form that is displayed: $Zeros, $Tens, $Twentys, $Thirtys etc.
I want to update the Labels on Form with values using the property Content.
So $Zeros.Content = 2 and so forth
foreach ($property in $Row_Details.PSObject.Properties) {
$property.Name >> $OutFile
# following creates the label names $Zeros,....
$v = -join('$',$property.Name)
# following gives error.. no property named Content
$v.Content = 2
# following gives error.. no property named Content
(-join('$',$property.Name)).Content = 2
# following gives error.. no property named Content
$v | Set-ItemProperty -Name "Content" -Value 2
# following does not update the Labels on the Form itself
Set-Variable $v -Value #{Content = "2"}
# Cannot use Set-ItemProperty -inputObject $v because cannot name property
}
I can hard code name of each Label, but was trying to do it dynamically or Elegantly...
Not sure I really understand your question, but I'll give it a try with a small example that might put you on a track to follow:
$object = New-Object PSObject -Property #{
Zeros = 0;
Tens = 0;
}
$Zeros = New-Object PSObject -Property #{
Content = "0"
}
$Tens = New-Object PSObject -Property #{
Content = "0"
}
$Zeros
$Tens
$object.PSObject.Properties | %{
$property = $_.Name
$expression = "`$$($property).Content = `"2`""
Invoke-Expression $expression
}
$Zeros
$Tens
The question is unclear to me, but maybe this helps
If you just want to modify the values for each of them you an do this:
foreach ($property in $Row_Details.PSObject.Properties)
{
$property.Value = 0
}
If you want to make new variables for each item:
foreach ($property in $Row_Details.PSObject.Properties)
{
New-Variable -Name $property.Name -Value $property.Value
}
You should be able to use Get-Variable for this is interact with those other objects. Shrinking your examples down to two you can proof of concept this fairly easily.
# Simulate your controls by creating object with those respective properties
$Zeros = New-Object PSObject -Property #{Content = 0}
$Tens = New-Object PSObject -Property #{Content = 0}
$Row_Details = New-Object PSObject -Property #{
Zeros = 1
Tens = 2
}
# Display the current Contents
write-host "Zeros: $($Zeros.Content)"
write-host "Tens: $($Tens.Content)"
foreach ($property in $Row_Details.PSObject.Properties){
$singleVariable = Get-Variable $property.name
$singleVariable.Value.content = $property.Value
}
# Show the updated Contents
write-host "Zeros: $($Zeros.Content)"
write-host "Tens: $($Tens.Content)"
The results of this being
Zeros: 0
Tens: 0
Zeros: 1
Tens: 2
The only problem I see is that all of this exists in the same scope so you my example should work as intended. However, depending where your variables are defined, you might have scope issues. If that happens you just need to experiment with the -Scope parameter of Get-Variable

Can't display PSObject

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

How to get powershell object properties in the same order that format-list does?

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

Find matches in two different Powershell objects based on one property

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 }