howdy y'all,
i vaguely recall seeing a post in this subreddit some time back that said the ArrayList had been deprecated. now i can't find it. [blush] the only things that show up on a net search are a couple of posts aimed at C# from several years ago.
so, am i misremembering things again?
take care,
lee
-ps
found the reason ... but i don't really understand it. lookee ...
ArrayList vs List<> in C# - Stack Overflow
-
http://stackoverflow.com/questions/2309694/arraylist-vs-list-in-c-sharp
lee-
I'm fully aware that ArrayList is not recommended for new development. That being said, do you still use it when you are writing PowerShell? I wish I could add a Poll to solicit the responses more easily, but Poll option is greyed out.
I've been roasted for using it before, but it's just so much more familiar and convenient to instantiate, for me.
[System.Collections.ArrayList]$list = @()
Slim and sexy, concise, looks like other PS syntax I'm used to.
[System.Collections.Generic.List[object]]::new()
Newfangled, unwieldy, uses C# constructor, ugly. Requires me to think about what types I'll have in my list. Smug.
(Obviously I'm feeling a little silly here / tongue in cheek, but I really do feel like I just don't want to bother getting used to the new way.)
EDIT: Some of the advice is helpful, but mostly what I was hoping to find out was whether I'm alone or whether you all use ArrayList. It's kind of like, I know I'm supposed to drink 8 glasses of water today, but I have a suspicion that few people actually do. So that's why I'm asking.
FINAL EDIT: After seeing that most people don't use ArrayLists anymore, I think I'm going to learn Lists and do things properly. Turns out I'm in the minority, at least on this sub. Thanks for the discussion everyone.
Videos
I am working with Cisco DNA Center (DNAC) to get a list of Network Devices and then pick out just the relevant data. I am able to get the full list of data from DNAC and I am testing creating a new arraylist with the 4 fields that I want from there. I am using $newlist = Testing -allnads $newinfo where $newinfo is the array of PSCustomObjects that I have gotten from DNAC.
Function Testing{
param($allnads)
$paredlist = New-Object System.Collections.ArrayList
foreach($device in $allnads){
$pareddown = @($device.hostname, $device.managementIpAddress, $device.id, $device.softwareversion)
$paredlist.add($pareddown)
}
return $paredlist
}$newinfo.count is 923 but then $paredlist.count is 1846
The first 923 indexes are all just the index value.
$newlist[0] is 0, $newlist[1] is 1, etc
$newlist[923] gives me the hostname, IP, GUID, and software version of the first switch which I would have expected at index 0.
Is there something stupid I am doing here (probably the case) or some bit of wizardry I need to know about before I just start any iterations through this data with
$i = $newlist.count/2
if($i -lt $newlist.count){... $i +=1}I am trying to understand when to us what.
I use generic.list a peer uses ArrayList.
Just curious.
Hi.
I writing scripts that involve multiple API and due to the sequential nature of the single-threaded approach, some take a lot of time to execute.
For a long time I used ArrayList and "+=" approach to create list of arrays but it seems that it's deprecated and not really a best practice. See LINK1 and LINK2.
So, until now, I used approach from LINK1: declare empty ArrayList, looped through the results of my API call with foreach and then added entries with +=.
Of course, it doesn't work with Foreach-Object -Parallel, because ArrayList isn't thread safe.
I tried using ConcurrentBag but I can't access my items like I did in the past.
I.e., if I declare:
$devices = [System.Collections.Concurrent.ConcurrentBag[PSObject]]::new()
I'd like to be able to:
-
$devices[0] - select the first device from the list
-
$devices | Where-Object {$_.SerialNumber -eq "ABC1234567890"}
Doing any of these returns all the results.
Is there any other structure, similar to ArrayList or am I doing something wrong here?
I'm trying to optimize a reporting script, and the biggest win is by using ArrayList instead of @() (other sections have gone from minutes to milliseconds) so I am trying to do the same here. But it doesn't work given the output from each query can be 0..n
Write-Host "STEP 7 " + $StopWatch.ElapsedMilliseconds
<# New arrays #>
$UserInboxRules = @()
$UserDelegates = @()
<#
$UserInboxRules = New-Object System.Collections.ArrayList
$UserDelegates = New-Object System.Collections.ArrayList
#>
Write-Host "STEP 8 " + $StopWatch.ElapsedMilliseconds
<# Build UserInboxRules and UserDelegates tables #>
ForEach ($entry in $MbxDb) {
$UserInboxRules += Get-InboxRule -Mailbox $entry.Email | Select-Object Name, Description, Enabled, Priority, ForwardTo, ForwardAsAttachmentTo, RedirectTo, DeleteMessage | Where-Object {($_.ForwardTo -ne $null) -or ($_.ForwardAsAttachmentTo -ne $null) -or ($_.RedirectsTo -ne $null)}
$UserDelegates += Get-MailboxPermission -Identity $entry.Email | Where-Object {($_.IsInherited -ne "True") -and ($_.User -notlike "*SELF*")}
<#
$UserInboxRulesEntry = Get-InboxRule -Mailbox $entry.Email | Select-Object Name, Description, Enabled, Priority, ForwardTo, ForwardAsAttachmentTo, RedirectTo, DeleteMessage | Where-Object {($_.ForwardTo -ne $null) -or ($_.ForwardAsAttachmentTo -ne $null) -or ($_.RedirectsTo -ne $null)}
$UserDelegatesEntry = Get-MailboxPermission -Identity $entry.Email | Where-Object {($_.IsInherited -ne "True") -and ($_.User -notlike "*SELF*")}
[void]$UserInboxRules.Add($UserInboxRulesEntry)
[void]$UserDelegates.Add($UserDelegatesEntry)
#>
}
Write-Host "STEP 9 " + $StopWatch.ElapsedMilliseconds
Any thoughts?
Edit: Formatted.
Howdy,
If I understand the problem correctly:
Each foreach loop query may return more than one object. When using the standard array with += it adds each object as a separate item. But when using the add method on the arraylist it is instead creating a jagged array inside of the arraylist (ie - instead of adding each object from the query separately, it is instead adding them all as a complete array for each loop)?
If so, have you tried using the arraylist addrange method instead?
edit: I just tested this and realized that if your result is a single item the addrange method will fail. I guess you could add some logic to test your results of the query with a if/elseif. If the result is a single object, use the add method. Elseif use the addrange method. Or maybe I just completely misunderstood the problem.... :-)
howdy PedroAsani,
as others have pointed out, += is NOT the thing to use with an arraylist. [grin] on my win7ps5.1 system that will convert the arraylist to a standard array ... and slow things down quite a bit.
plus, arraylists are both deprecated AND rather annoying - all those [voids] to suppress unwanted "i added something!" msgs.
so, you are recommended to use a generic.list - and that does NOT generate the silly "added something" stuff. [grin]
however, if you are not going to change the size of the array very often, then feeding the loop [foreach, ForEach-Object, While, any pipeline output, or any other loop] directly to a $Var is the fastest. lookee ...
$RepeatCount = 1e4'Direct from Foreach = {0,10:N3} milliseconds' -f (Measure-Command -Expression { $DFF_Result = foreach ($Item in 1..$RepeatCount) { $Item } }).TotalMilliseconds'Array add = {0,10:N3} milliseconds' -f (Measure-Command -Expression { $Ar_Result = @() foreach ($Item in 1..$RepeatCount) { $Ar_Result += $Item } }).TotalMilliseconds'ArrayList add = {0,10:N3} milliseconds' -f (Measure-Command -Expression { $AL_Result = [System.Collections.ArrayList]::new() foreach ($Item in 1..$RepeatCount) { $Null = $AL_Result.Add($Item) } }).TotalMilliseconds'Generic.List add = {0,10:N3} milliseconds' -f (Measure-Command -Expression { $GL_Result = [System.Collections.Generic.List[int]]::new() foreach ($Item in 1..$RepeatCount) { $GL_Result.Add($Item) } }).TotalMilliseconds
output ...
Direct from Foreach = 23.432 millisecondsArray add = 3,831.392 millisecondsArrayList add = 31.171 millisecondsGeneric.List add = 34.799 milliseconds
hope that helps,lee
Edit: Solved
It's much more flexible to use generic lists instead of arrays. Arrays are immutable and should not be used when there is a need to add or remove elements. Another option is to use array lists, but others reported that they are deprecated and that generic lists should be used instead.
Thank you all for the help!
-------------
PowerShell 7, an array like $array = @()
Like the title say - is there?
The solutions I've found online are all wrong.
- Array slicing
$array = $array[0..($array.Length - 2)]
This does not work if the array length is 1, because it resolves to $array[0..-1]. Step-by-step debugging shows that instead of deleting the last remaining element of the array, it will duplicate that element. The result will be an array of 2 elements, not 0.
- Select-Object
$array = $array | Select-Object -SkipLast 1
This does not work well with Hashtables as array elements. If your array elements are Hashtables, it will convert them to System.Collections.Hashtable. Hashtable ($example = @{}) and System.Collection.Hashtable are not the same type and operations on those two types are different (with different results).
Edit for the above: There was a typo in that part of my code and it returned some nonsense results. My bad.
- System.Collections.ArrayList
Yes, you can convert an array to System.Collection.ArrayList, but you are then working with System.Collections.ArrayList, not with an array ($array = @()).
----------------
One solution to all of this is to ask if the array length is greater than one, and handle arrays of 1 and 0 elements separately. It's using an if statement to simply remove the last element of an array, which is really bad.
Another solution is to loop through an array manually and create a new one while excluding the last element.
And the last solution that I've found is not to use arrays at all and use generic lists or array lists instead.
Is one of these options really the only solution or is there something that I'm missing?
Code: https://pastebin.com/VQQUFJEM
Hey,
Im having issues where, the new value received in the loop is not being added to a new index in the ArrayList.
When I print out the value of the ArrayList, all the items are added into one index, like a string.
I've come across two ways of creating a new System.Collections.ArrayList object:
$arrA = New-Object System.Collections.ArrayList $arrB = [System.Collections.ArrayList]@()
I have two questions:
-
for arrB, if I'm reading this right, @() is creating an empty array and then it's being cast into [System.Collections.ArrayList]?
-
Working with the created object is the same for me either way, but are there any differences I may be missing?
I made a quick test and casting was faster, but the difference is only noticeable when creating new arrays reaches the hundreds of thousands to millions (tested on an old A10-7870k). Doesn't really matter when I'm creating one array, but I thought it was mildly interesting.
$max = 100000
$ticksStart = (Get-Date).Ticks
for ($i = 0; $i -lt $max; $i++)
{
$arrA = New-Object System.Collections.ArrayList
}
$ticksEnd = (Get-Date).Ticks
Write-Host 'arrA (ticks) : ' ($ticksEnd - $ticksStart)
$ticksStart = (Get-Date).Ticks
for ($i = 0; $i -lt $max; $i++)
{
$arrB = [System.Collections.ArrayList]@()
}
$ticksEnd = (Get-Date).Ticks
Write-Host 'arrB (ticks) : ' ($ticksEnd - $ticksStart)
arrA (ticks) : 64310000
arrB (ticks) : 3170000I have following problem.
I have an arraylist. This arraylist consists of 2 Entries
User1User2
However, If I use this arraylist within a $body=@"..."@ the arraylist is reduced to 1 line.
I want the $ararylist to be 2 lines (for this example), not 1 line.
Code Example is attached to show what I mean.
The $Arraylist within $body should be the same as outside the $body. (meaning adding 2 lines to the $body.
$Parameters = "User1","User2"
$Arraylist = New-Object System.Collections.ArrayList
$i = 1
Foreach ($Parameter in $Parameters){
$Line = "String"+$i+" =" + $Parameter
$Arraylist.Add($Line) | Out-Null
$i++
}
$Body = @"
Body begins here
string0 = Bla
Arraylist in 1 line instead of 2
$Arraylist
"@
$Arraylist
$bodyoutput of $Body is:
Body begins here
string0 = Bla
Arraylist in 1 line instead of 2
String1 =User1 String2 =User2
Output should be:
Body begins here
string0 = Bla
Arraylist in 1 line instead of 2
String1 =User1
String2 =User2
Any help is appreciated.
Could it be because of the new version on powershell that when i'm trying to add a string to an array it doesn't work with the method of .add and .remove?
Hey guys, Nathan Kasco just wrote a shiny new blog post you may enjoy.
Summary: Get back to PowerShell basics learning how to use PowerShell arraylists and basic arrays in this how-to walkthrough!
https://adamtheautomator.com/powershell-arraylist/?utm_source=redditpowershell&utm_medium=social&utm_campaign=newblogpostnotifications
Let's say I loop through a collection of computers, retrieve some information here and there, create a hastable out of that information and add it to an array.
$file = Get-Content $pathtofile
$output = @()
[PSCustomObject]$h = @{}
foreach ($item in $file){
$h."Name" = $item
...other properties...
$output += $h
}I understand that adding to an array this way will destroy the array upon each iteration to create it anew. I understand that when dealing with very large amounts of data, it can lead to longer processing times.
But aside from that, why is it a bad idea? I've never had errors come out of using this (using PS 5.1), and always found it handy. But I feel like there's something i'm missing...
Today I was messing around with arrays, arraylists, and generic lists. I'm also curious to know more about their advantages and inconvients, which I find closely related to using += or methods.
I have a script that look at our Azure AD environment and pulls out any expiring certificates or secrets and puts them into an array which is exported as a csv at the end for reporting purposes. Because each application can have multiple secrets/certificates, the way I'm currently doing this is to have nested for each loops (one for Applications, and then separate loops within the ForEach Application loop looking at each certificate and secret), and inside each loop, recreate the whole record for the application just to add the expiring secret/certificate to the array I'm using to create the report (since every record in the array has to contain all the same properties as other records, I have to add the certificate start date even if I'm looking only at client secrets etc.).
This is just incredibly messy and inefficient and I'm wondering if there's a better way to do this that doesn't mean having to recreate the entire array in every different loop.
For example:
$Log = New-Object System.Object
$Log | Add-Member -MemberType NoteProperty -Name "ApplicationName" -Value $AppName
$Log | Add-Member -MemberType NoteProperty -Name "ApplicationID" -Value $ApplID
$Log | Add-Member -MemberType NoteProperty -Name "Secret Start Date" -Value $null
$Log | Add-Member -MemberType NoteProperty -Name "Secret End Date" -value $null
$Log | Add-Member -MemberType NoteProperty -Name "Certificate Start Date" -Value $CStartDate
$Log | Add-Member -MemberType NoteProperty -Name "Certificate End Date" -value $CEndDate
$Log | Add-Member -MemberType NoteProperty -Name "SAML Certificate Start Date" -Value $null
$Log | Add-Member -MemberType NoteProperty -Name "SAML Certificate End Date" -value $null
$Log | Add-Member -MemberType NoteProperty -Name "Owner" -Value $Username
$Log | Add-Member -MemberType NoteProperty -Name "Owner_ObjectID" -value $OwnerID
$Logs += $Log
Currently this block of code is copy/pasted 5 separate times in my script, just with different values nulled out depending on what that loop is looking at. The only other way I could think to do this would be to put all the expiring secrets/certificates for each application in their own array, and then just generate the record in the application loop with those arrays as the values for the start/end dates for the certs/secrets, rather than having a separate record for each certificate and each secret etc, but I'm open to hearing any suggestions!
Thanks
Simplifying your script can make it more efficient.
# Define a function to create an application log object
function New-ApplicationLog {
param (
[string]$AppName,
[string]$ApplID,
[DateTime]$CStartDate,
[DateTime]$CEndDate,
[string]$Username,
[string]$OwnerID
)
$log = New-Object PSObject -Property @{
ApplicationName = $AppName
ApplicationID = $ApplID
SecretStartDate = $null
SecretEndDate = $null
CertificateStartDate = $CStartDate
CertificateEndDate = $CEndDate
SAMLStartDate = $null
SAMLEndDate = $null
Owner = $Username
Owner_ObjectID = $OwnerID
}
return $log
}
# Initialize an array to store application logs
$Logs = @()
# Example usage within your loop
foreach ($Application in $Applications) {
# Retrieve necessary information for the application
$AppName = $Application.Name
$ApplID = $Application.ID
$CStartDate = $Application.CertificateStartDate
$CEndDate = $Application.CertificateEndDate
$Username = $Application.Owner
$OwnerID = $Application.OwnerID
# Create an application log object
$Log = New-ApplicationLog -AppName $AppName -ApplID $ApplID -CStartDate $CStartDate -CEndDate $CEndDate -Username $Username -OwnerID $OwnerID
# Add the log object to the array
$Logs += $Log
}
# Export the array to a CSV file
$Logs | Export-Csv -Path "Path\To\Your\Report.csv" -NoTypeInformation
hopefully this helps you!
There are various ways to deal with this
To start, use ArrayLists, not Arrays.
Second, have some code I wrote on the fly.
## first two elements are outside the various loops and repetitions
# this will contain the various logs. Longer, easier-to-read name
$LogList = [System.Collections.ArrayList]::new()
# this is the template of your logs.
# as Hashtable so it gets cloned, not linked, to the various iterations
$LogTemplate = [hashtable]@{
'ApplicationName' = $null
'ApplicationID' = $null
'Secret Start Date' = $null
'Secret End Date' = $null
'Certificate Start Date' = $null
'Certificate End Date' = $null
'SAML Certificate Start Date' = $null
'SAML Certificate End Date' = $null
'Owner' = $null
'Owner_ObjectID' = $null
}
##
## This part will be in the various repetitions
# copy the template as a PSCustomObject
$logSingle = [pscustomobject]$LogTemplate
# Modify ONLY the values you need.
# Everything else will stay at $null value
$LogSingle.ApplicationID = 1
# add the log to the list
# [ArrayLisy].Add() returns the index position of the added item.
# There are a number of easy workaround if that's a problem.
$LogList.add($LogSingle)
##
## outside the repetitions
# here you have your ArrayList, ready to be converted to CSV, directly exported, or whatever you want
$LogList