0

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.

11
  • What code have you tried. How has it not worked? Did you get errors? Were the values wrong? Did the code you wrote do nothing? Commented Mar 2, 2018 at 15:24
  • 1
    Or quoting the words of @EBGreen, "Are wombats appearing at your desk and eating the files?" Commented Mar 2, 2018 at 15:25
  • And to be a little more specific, what code have you written to create the objects and add them to the array? Commented Mar 2, 2018 at 15:26
  • It's a valid question @VivekKumarSingh and I'm glad someone is finally asking it. Commented Mar 2, 2018 at 15:27
  • 1
    Aaaahhh...I understand, you want to create an array that already has objects in it then just go down the line setting property values. There is not a built in way to do this but the patterns you have been shown as near as I can tell will have the same computational requirements. Commented Mar 2, 2018 at 16:10

6 Answers 6

4

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Nice. This is even closer to what the OP wants than what I posted I imagine. I have not played with 5+ enough yet apparently.
Also I have not done any benchmarking but I'm pretty sure the generic List is probably about as fast as ArrayLists.
Yes, this nails it. Thank you!
My pleasure, SaintFrag. @EBGreen: That would be an interesting comparison. Incidentally, native PowerShell list support is being discussed, though that would use [System.Collections.Generic.List[object]], and with [object] as the element type I wouldn't expect a performance gain (though I don't know).
@mklement0 good to know. As I pointed out in another comment though, I still have to write a lot of backwards compatible scripts. :( Often as far back as 2.0 depending on who I'm writing them for. Coporate inertia is a pain...
0

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.

3 Comments

You don't need the Add-member statements $WIP += new-object psobject -property @{Signature='data'; PID='data'}
I like that HeedfulCrayon! I will steal that little tidbit.
Also, it is more efficient (if you are using a loop to create these objects to do it this way: $WIP = @( for ($i = 0; $i -lt 50; $i++) { New-Object psobject -Property { Signature='data' PID='data' } }) This way it creates all the objects and then puts them into the array rather than one by one
0

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.

5 Comments

Unfortunately, that's not a dynamic array. So $list.length is going to return 100.
I'm not sure what you mean by dynamic. Traditionally a dynamic array is an array that can be resized. Powershell arrays behave as if they are dynamic (that is why += resizes them) even though technically they aren't. Rgardless, you can add new objects to a powershell array and you then have an array that is one element larger than it was before you added the object. What do you mean by dynamic?
Sorry...So doing $arr = @() would be a dynamic array in the sense that it's fully flexible and empty. Whereas here you have to predefine the size. Dynamic is probably not the proper term. Perhaps I should have said I need to be able to create an empty array... the structure... to be filled dynamically as I go.
Correct. However if you are going to be adding a lot of elements then System.Collections.ArrayList would be faster. .Net arrays are technically not dynamic so when you resize the array under the hood it is actually creating a new array of size +1 then putting all the existing elements into the new array plus the new element then destroying the old array. For trivial sizes this is not an issue. ArrayLists are actually just moving pointers I believe. Regardless they are much faster for large element counts.
@() does create an empty array that you can fill as you go. This is a very common pattern in Powershell.
0

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

Comments

0

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]

6 Comments

The only downside is that I can't simply call $array.length to find the 'in use' items. I would guess there's a way to run the invoke-expression any time I need to add to the array, rather than doing it up front. That way the .length is always correct. Thoughts?
In Delphi the .length property returns the current item you are iterating and not the actual total length of an array?
No, but in Delphi I could create the empty array structure and just add to it (like mklement0's answer). So the .length was always correct.
Oh, well arrays work that way too as long as you account for them being 0 indexed. Regardless mklement0's answer definitely comes the closest to what you wanted. It is a good pattern unfortunately I still have to write enough backwards compatible scripts that I can't use it on a day to day basis. :(
Right, but with your solution, $array.length will always return 100. So it's like 99% there for what I needed. That's why I was curious about running the Invoke-Expression bit as I went, which would resolve that issue. I greatly appreciate your help on this, even if mklement0 swooped in. :)
|
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

4 Comments

While this will technically sort of work (do you have a background in PHP). Using an array of custom objects is more idiomatic for powershell.
@EBGreen I just find usage of custom objects cumbersome. What are the advantages of using custom objects in this case? I have background all over the place. My code is almost always idiomatically inappropriate.
Meh, nothing wrong with your method. It's an admin scripting language so at the end of the day if it works for you then you did it right. I asked about PHP because it is the only language that I know of where arrays are actually dictionaries with numeric keys.
Well, thanks for the comment anyway. Oh, I didn't know that about PHP. You learn something every day.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.