Can I create a custom PowerShell object with an array property? - arrays

Warning: I am looking to do this in PowerShell v2 (sorry!).
I would like to have a custom object (possibly created as a custom type) that has an array property. I know how to make a custom object with "noteproperty" properties:
$person = new-object PSObject
$person | add-member -type NoteProperty -Name First -Value "Joe"
$person | add-member -type NoteProperty -Name Last -Value "Schmoe"
$person | add-member -type NoteProperty -Name Phone -Value "555-5555"
and I know how to make a custom object from a custom type:
Add-Type #"
public struct PersonType {
public string First;
public string Last;
public string Phone;
}
"#
$person += New-Object PersonType -Property #{
First = "Joe";
Last = "Schmoe";
Phone = "555-5555";
}
How can I create a custom object with a type that includes an array property? Something like this hash table, but as an object:
$hash = #{
First = "Joe"
Last = "Schmoe"
Pets = #("Fluffy","Spot","Stinky")
}
I'm pretty sure I can do this using [PSCustomObject]$hash in PowerShell v3, but I have a need to include v2.
Thanks.

When you use Add-Member to add your note properties, the -Value can be an array.
$person | add-member -type NoteProperty -Name Pets -Value #("Fluffy","Spot","Stinky")
If you want to create the properties as a hashtable first, like your example, you can pass that right into New-Object as well:
$hash = #{
First = "Joe"
Last = "Schmoe"
Pets = #("Fluffy","Spot","Stinky")
}
New-Object PSObject -Property $hash
Your PersonType example is really written in C#, as a string, which gets compiled on the fly, so the syntax will be the C# syntax for an array property:
Add-Type #"
public struct PersonType {
public string First;
public string Last;
public string Phone;
public string[] Pets;
}
"#

Related

Array of custom variable with two properties

I feel like my Delphi background is destroying my ability to figure this out. I'm trying to create an empty array (no data, just structure) in Powershell where each item has two properties. The end result would look something like this:
$WIP[0].signature = 'data'
$WIP[0].PID = 'data'
# other fake stuff in between
Write-host "Item 43 has signature: " $WIP[43].signature
For some reason, I'm roadblocking on every attempt to create what should be simple to do. Thoughts?
Update to answer questions
I know some people do similar to the following, but this isn't as flexible as I'd like:
$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
This requires the values to be present for all three members when creating each $object. I was thinking the init would look more along the lines of (pseudocode):
$MyRecord = {
Signature as string
PID as integer
}
$RecArray = array of $MyRecord
That's notably a bad mashup of Delphi and Powershell. But would create a fully structured array, addressable as noted up top.
A PSv5+ solution that uses a PS class and a generic list ([System.Collections.Generic.List[]]) to store the instances (loosely speaking, an array that can grow efficiently).
# Your custom type.
class MyRecord {
[string] $Signature
[int] $PID
}
# If you want a list that can grow efficiently,
# use [System.Collections.Generic.List[]]
$RecList = [System.Collections.Generic.List[MyRecord]]::new()
# Add a new instance...
$RecList.Add([MyRecord]::new())
# ... and initialize it.
$RecList[0].Signature = 'sig1'
$RecList[0].Pid = 666
# ... or initialize it first, and then add it.
# Note the use of a cast from a hashtable with the property values.
$newRec = [MyRecord] #{ Signature = 'sig2'; PID = 667}
$RecList.Add($newRec)
# Output the list
$RecList
The above yields:
Signature PID
--------- ---
sig1 666
sig2 667
As for removing objects from the list:
To remove by index, use .RemoveAt(); an out-of-range index throws an error:
$RecList.RemoveAt(1)
To remove by object already stored in the list, use .Remove().
Note that the [bool] return value indicates whether the value was
actually removed (if the object wasn't in the list, the operation is
a no-op and $False is returned)
$actuallyRemoved = $RecList.Remove($newRec)
For details, see the docs.
You want to create a custom object.
You create an object that has all the properties you need. Then you create a collection, and you stuff instances of your new object into the collection. Here's an example:
$WIP = #()
$o = New-Object –TypeName PSObject
Add-Member -InputObject $o –MemberType NoteProperty –Name signature –Value 'foo'
Add-Member -InputObject $o –MemberType NoteProperty –Name data –Value 'bar'
$WIP += $o
$WIP[0].signature
$WIP[0].data
You'd need to execute the New-Object and Add-Member statements for each object you're creating.
So here's working example of how You can get something like this working:
$list=#()
1..100|foreach-object{
$obj=""|select signature,pid
$obj.signature="$_ signature"
$obj.pid="$_ PID"
$list+=$obj
}
With the object created this way - You can do $list[43].signature and it does work.
What exactly do you mean by "Dynamic"?
$array = #(
# Some type of loop to create multiple items foreach/for/while/dowhile
foreach ($item in $collection) {
New-Object psobject -Property #{
Signature = 'data'
PID = 'data'
}
}
)
Or you can manually add objects like so
$array = #()
# Later in code
$array += New-object psobject #{
Signature = 'data'
PID = 'data'
}
Then you can access each item like so:
$array[1].Signature
$array[1].PID
There is no real difference between this an what you have already been shown but I think this gives you what you are asking for (even though it is not the powershelly way to do things).
$object = "New-Object PSCustomObject -Property #{'Name' = ''; 'Age' = [int]}"
$array = 1..100 | %{Invoke-Expression $object}
$array[0].Name = 'Joe'
$array[0].Age = 12
$array[0]
You can use a hashtable with indices as keys, and your hashtable as values. It's pretty easy to work with.
$WIP = #{
0 = #{
signature = 'signature 0'
PID = 'PID 0'
}
1 = #{
signature = 'signature 1'
PID = 'PID 1'
}
}
You can add any index you want.
$WIP[12] = #{
signature = "signature 12"
PID = "PID 12"
}
$WIP[12].PID
# PID 12
You can initialize both, any, or none.
$WIP[76] = #{
signature = "signature 76"
}
$WIP[76].signature
# signature 76
$WIP[76].PID
# $null
Count gives you number of "active" elements.
$WIP.Count
# 4

Update object array over multiple iterations

I have an array of custom objects:
$report = #()
foreach ($person in $mylist)
{
$objPerson = New-Object System.Object
$objPerson | Add-Member -MemberType NoteProperty -Name Name -Value $person.Name
$objPerson | Add-Member -MemberType NoteProperty -Name EmployeeID
$objPerson | Add-Member -MemberType NoteProperty -Name PhoneNumber
$report += $objPerson
}
Note that I haven't set values for the last two properties. The reason I've done this is because I'm trying to produce a matrix where I'll easily be able to see where these are blanks (although I could just set these to = "" if I have to).
Then, I want to iterate through a second dataset and update these values within this array, before exporting the final report. E.g. (this bit is pretty much pseudo code as I have no idea how to do it:
$phonelist = Import-Csv .\phonelist.csv
foreach ($entry in $phonelist)
{
$name = $entry.Name
if ($report.Contains(Name))
{
# update the PhoneNumber property of that specific object in the array with
# another value pulled out of this second CSV
}
else
{
# Create a new object and add it to the report - don't worry I've already got
# a function for this
}
}
I'm guessing for this last bit I probably need my if statement to return an index, and then use that index to update the object. But I'm pretty lost at this stage.
For clarity this is a simplified example. After that I need to go through a second file containing the employee IDs, and in reality I have about 10 properties that need updating all from different data sources, and the data sources contain different lists of people, but with some overlaps. So there will be multiple iterations.
How do I do this?
I would read phonelist.csv into a hashtable, e.g. like this:
$phonelist = #{}
Import-Csv .\phonelist.csv | ForEach-Object { $phonelist[$_.name] = $_.number }
and use that hashtable for filling in the phone numbers in $report as you create it:
$report = foreach ($person in $mylist) {
New-Object -Type PSObject -Property #{
Name = $person.Name
EmployeeID = $null
PhoneNumber = $phonelist[$person.Name]
}
}
You can still check the phone list for entries that are not in the report like this:
Compare-Object $report.Name ([array]$phonelist.Keys) |
Where-Object { $_.SideIndicator -eq '=>' } |
Select-Object -Expand InputObject
I would iterate over the $phonelist two times. The first time, you could filter all phone entities where the name is in your $myList and create the desired object:
$phonelist = import-cse .\phonelist.csv
$report = $phonelist | Where Name -in ($mylist | select Name) | Foreach-Object {
[PSCustomObject]#{
Name = $_.Name
PhoneNumber = $_.PhoneNumber
EmployeeID = ''
}
}
The second time you filter all phone entities where the name is not in $myList and create the new object:
$report += $phonelist | Where Name -NotIn ($mylist | select Name) | Foreach-Object {
#Create a new object and add it to the report - don't worry I've already got a function for this
}

Remove one or many members from Object in powershell

I have created a custom object called $info and moving it to an array $arr ,
How is it possible to remove one member along with its all properties ?
My script:
Get-Process | ForEach-Object{
$info = New-Object -TypeName PSObject
$info | Add-Member -Type NoteProperty -Name Process -Value $_.processname
$info | Add-Member -Type NoteProperty -Name ID -Value $_.id
$arr += $info
}
$arr | ft -AutoSize
The result looks like this :
Process ID
------- --
ApplicationFrameHost 38556
AppVShNotify 9792
armsvc 2336
atieclxx 6944
atiesrxx 1844
audiodg 59432
CcmExec 3988
chrome 46068
How can I remove one particular member for example "audiodg 59432" gets removed
audiodg 59432
Your terminology is a bit incorrect here. A member is on an individual object. When you use Add-Member above you're adding properties to each individual object, then you're returning an array of objects.
You're asking how to remove an individual object from the array.
In PowerShell you cannot remove an item from an array. You could instead filter the array based on some criteria and create a new one:
$newArr = $arr | Where-Object { $_.Name -ne 'audiodg' }
# or
$newArr = $arr | Where-Object { $_.ID -ne 59432 }

How to match a value with my custom-object array

I'm having dificulties working with an array of custom objects and matching it with a variable.
I have a variable: $CmbCust.SelectedItem (currently selected item in a WPF form)
Custom-Object and the creation of my items in the combobox:
$CustomerFileArray = #()
ForEach ($c in (Get-ChildItem $ProgramRoot\Customers -Filter Customer-*.xml | sort Name -descending)) {
$XmlCustomer = [xml](Get-Content $ProgramRoot\Customers\$c)
if ($XmlCustomer.Office365.Customer.Name -eq "") {
$CustomerName = "- Geen naam"
}
Else {
$CustomerName = $XmlCustomer.Office365.Customer.Name
}
$CustomerItem = New-Object PSObject
$CustomerItem | Add-Member -type NoteProperty -Name 'Name' -Value $CustomerName
$CustomerItem | Add-Member -type NoteProperty -Name 'File' -Value $c
$CustomerFileArray += $CustomerItem
[void] $CmbCust.Items.Add($CustomerName)
}
$CmbCust.SelectedItem = $XmlOffice365.Office365.Customer.Name
My question is, how can I match the value in $CmbCust.SelectedItem with my Array $CustomerFileArray's File property
The action I would like to do is to get a path of the selected item to remove it. I've Googled and came up with:
$RemoveFile = #()
$RemoveFile | where {$CustomerFileArray.ContainsKey($_.CmbCust.SelectedItem)}
Remove-Item $ProgramRoot\Customers\$RemoveFile -Force
But that doesn't seem to work...
Thanks in advance!
Since the SelectedItem property contains a string found in the Name property of one of the items in $CustomerFileArray, you should apply Where-Object to $CustomerFileArray like so:
$CustomerFileArray |Where-Object {$_.Name -eq $CmbCust.SelectedItem} |Select-Object -ExpandProperty File
This will return the FileInfo object you originally assigned to the File property of the corresponding object in the array

Powershell add-member. Add a member that's an ArrayList?

The Powershell "add-member" command is very useful. I use it to add properties to custom objects. Sometimes I set a member as an array to hold multiple objects. Is it possible to add an ArrayList as a member on a custom object?
Imagine a list of articles has properties "index", "title", and "keywords." In Powershell, you could put this code in a loop:
for($i = 0; $i -lt 100; $i++) {
$a = new-object -TypeName PSObject
$a | Add-Member -MemberType NoteProperty -Name index -Value $i
$a | Add-Member -MemberType NoteProperty -Name title -Value "Article $i"
$a | Add-Member -MemberType NoteProperty -Name keywords -Value #()
$articles += $a
}
You'd end up with an array, $articles, of Article objects, each with members index, title, and keywords. Furthermore, the keywords member is an array that can have multiple entries:
$articles[2].keywords += "Stack Exchange", "Powershell", "ArrayLists"
$articles[2].keywords[2]
Powershell
This meets most of my needs, but I just don't like dealing with arrays. ArrayLists are just easier to work with, if only because
$arrayList1.remove("value")
is so much more intuitive than
$array1 = $array1 |? {$_ new "value"}
Is there a way with Add-Member to add an ArrayList as a member? Or am I stuck with arrays? If Powershell doesn't support thi snatively, could I pop in some C# code to make a new class with an ArrayList as a member?
$arr = #("one","two","three")
$arr.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
$a = new-object -TypeName PSObject
[System.Collections.ArrayList]$arrList=$arr
$a | Add-Member -MemberType NoteProperty -Name ArrayList -value $arrlist
$a.ArrayList
one
two
three
$a.ArrayList.remove("one")
$a.ArrayList
two
three
To add a blank ArrayList to your custom object just use
$a | Add-Member -MemberType NoteProperty -Name ArrayList -value (New-object System.Collections.Arraylist)
I find all this Add-Member stuff to be confusing for data only. In powershell 3, you can just make an object from a hashtable, and use a little thing I learned from a blog about using Invoke to get a Collection typed object:
$myObject = [PSCustomObject]#{
index = $idx;
title = $title;
keywords = {#()}.Invoke()
}
$myObject.keywords.Add("foo")
$myObject.keywords.Add("bar")
Write-Host "Original"
$myObject.keywords
Write-Host
Write-Host "New:"
[void]$myObject.keywords.Remove("foo")
$myObject.keywords
Write-Host
Original
foo
bar
New:
bar

Resources