First, as this leads to my question, I'll start by noting that I've worked with XML a fair bit in PowerShell, and like how I can read data from XML files, quickly, into arrays of custom objects. For example, if I had the following XML file:
<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>
And if I read it in simply, like this:
[xml]$myxml = Get-Content .\my.xml
Then I can get to an array of my items like this:
[array]$myitems = $myxml.stuff.Item
$myitems
name age info
---- --- ----
Joe 32 something about him
Sue 29 something about her
Cat 12 something else
So, now my question:
How can I create a similar structure of an array of custom objects, and initialize them in my script, without reading a file?
I can do lots of looping and/or lots of creating/initializing individual objects, and then add to an array one at a time...
But it seems there should be a way to perform this creation/initialization in a simpler way. Note that the key here, is that my custom objects have more than two elements (otherwise, I'd have used a hash).
I've even looked at creating a big string of XML, and using Select-XML, but I just couldn't get the syntax right (if that was even the right road to be heading down).
This question is related to
powershell
pscustomobject
Maybe you mean like this? I like to make an object and use Format-Table:
> $array = @()
> $object = New-Object -TypeName PSObject
> $object | Add-Member -Name 'Name' -MemberType Noteproperty -Value 'Joe'
> $object | Add-Member -Name 'Age' -MemberType Noteproperty -Value 32
> $object | Add-Member -Name 'Info' -MemberType Noteproperty -Value 'something about him'
> $array += $object
> $array | Format-Table
Name Age Info
---- --- ----
Joe 32 something about him
This will put all objects you have in the array in columns according to their properties.
Tip: Using -auto
sizes the table better
> $array | Format-Table -Auto
Name Age Info
---- --- ----
Joe 32 something about him
You can also specify which properties you want in the table. Just separate each property name with a comma:
> $array | Format-Table Name, Age -Auto
Name Age
---- ---
Joe 32
The simplest way to initialize an array
Create array
$array = @()
Create your header
$line = "" | select name,age,phone
Fill the line
$line.name = "Leandro"
$line.age = "39"
$line.phone = "555-555555"
Add line to $array
$array += $line
Result
$array
name age phone
---- --- -----
Leandro 39 555-555555
Here is a concise way to initialize an array of custom objects in PowerShell.
> $body = @( @{ Prop1="1"; Prop2="2"; Prop3="3" }, @{ Prop1="1"; Prop2="2"; Prop3="3" } )
> $body
Name Value
---- -----
Prop2 2
Prop1 1
Prop3 3
Prop2 2
Prop1 1
Prop3 3
A little variation on classes. Initialize it with hashtables.
class Point { $x; $y }
$a = [Point[]] (@{ x=1; y=2 },@{ x=3; y=4 })
$a
x y
- -
1 2
3 4
$a.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Point[] System.Array
$a[0].gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Point System.Object
Use a "Here-String" and cast to XML.
[xml]$myxml = @"
<stuff>
<item name="Joe" age="32">
<info>something about him</info>
</item>
<item name="Sue" age="29">
<info>something about her</info>
</item>
<item name="Cat" age="12">
<info>something else</info>
</item>
</stuff>
"@
[array]$myitems = $myxml.stuff.Item
$myitems
I had to create an array of a predefined type, and I successfully did as follows:
[System.Data.DataColumn[]]$myitems = ([System.Data.DataColumn]("col1"),
[System.Data.DataColumn]("col2"), [System.Data.DataColumn]("col3"))
Given the data above, this is how I would do it:
# initialize the array
[PsObject[]]$people = @()
# populate the array with each object
$people += [PsObject]@{ Name = "Joe"; Age = 32; Info = "something about him" }
$people += [PsObject]@{ Name = "Sue"; Age = 29; Info = "something about her" }
$people += [PsObject]@{ Name = "Cat"; Age = 12; Info = "something else" }
The below code will work even if you only have 1 item after a Where-Object
:
# display all people
Write-Host "People:"
foreach($person in $people) {
Write-Host " - Name: '$($person.Name)', Age: $($person.Age), Info: '$($person.Info)'"
}
# display with just 1 person (length will be empty if using 'PSCustomObject', so you have to wrap any results in a '@()' as described by Andrew Savinykh in his updated answer)
$youngerPeople = $people | Where-Object { $_.Age -lt 20 }
Write-Host "People younger than 20: $($youngerPeople.Length)"
foreach($youngerPerson in $youngerPeople) {
Write-Host " - Name: '$($youngerPerson.Name)'"
}
Result:
People:
- Name: 'Joe', Age: 32, Info: 'something about him'
- Name: 'Sue', Age: 29, Info: 'something about her'
- Name: 'Cat', Age: 12, Info: 'something else'
People younger than 20: 1
- Name: 'Cat'
Here is a more concise version of the accepted answer which avoids repeating the NoteProperty identifiers and the [pscustomobject]
-cast:
$myItems = ("Joe",32,"something about him"), ("Sue",29,"something about her")
| ForEach-Object {[pscustomobject]@{name = $_[0]; age = $_[1]; info = $_[2]}}
Result:
> $myItems
name age info
---- --- ----
Joe 32 something about him
Sue 29 something about her
Source: Stackoverflow.com