Adding arrays to arraylists
Powershell Convert String[] to List<String> - Stack Overflow
How to create an ArrayList from an Array in PowerShell? - Stack Overflow
When to use array vs array list vs list
Videos
u/omers made a good post about Generic.Lists, how they're different to arrays, and when and why you should use them. I'd recommend reading it if you haven't already.
But the post didn't go over one thing, how to create a Generic.List. The most common way, if not the only way I've seen it done is by creating a new list using New-Object, then iterating through an array using foreach or ForEach-Object and adding each element to the list individually.
This has two problems, New-Object is slower than using a type's constructor. And iterating through an array is wasteful.
Instead, [System.Collections.Generic.List]'s constructors should be used. Consider this code:
[Byte[]]$Bytes = 1..100000 | ForEach-Object {Get-Random -Minimum 0 -Maximum 256}
$Output = [PSCustomObject]@{}
$Output | Add-Member -MemberType NoteProperty -Name 'QuickMethod' -Value (
Measure-Command {
$QuicklyCreatedGenericList = [System.Collections.Generic.List[Byte]]::New($Bytes)
}
)
$Output | Add-Member -MemberType NoteProperty -Name 'QuickList' -Value $QuicklyCreatedGenericList
$Output | Add-Member -MemberType NoteProperty -Name 'SlowMethod' -Value (
Measure-Command {
$SlowlyCreatedGenericList = New-Object 'System.Collections.Generic.List[Byte]'
foreach ($Byte in $Bytes)
{
$SlowlyCreatedGenericList.Add($Byte)
}
}
)
$Output | Add-Member -MemberType NoteProperty -Name 'SlowList' -Value $SlowlyCreatedGenericList
$Output
This code generates an array of 100,000 random bytes. It then converts the array to a Generic.List using two different methods, the first using [System.Collections.Generic.List]'s constructor, and the second creating a list using New-Object, and iterating through the array, adding each element individually.
The two lists are identical, but the time it took the generate the lists is not. On my machine, creating a list and adding the elements took 196 milliseconds - not too bad, but - using the constructor took 0.0824 milliseconds, or 0.042% the amount of time it took the second method.
Another advantage of this is that it allows you to assign Generic.Lists like arrays, so instead of having a fixed-size:
[String[]]$Gospel = 'PowerShell', 'is', 'good'
you can have a variable-size:
$Gospel = [Collections.Generic.List[String]]::New( [String[]]( 'PowerShell', 'is', 'great' ) )
As noted by u/TheIncorrigible1, you can also cast an array to a list:
$Gospel = [Collections.Generic.List[String]]@('PowerShell', 'is', 'brilliant')TL;DR?
[Collections.Generic.List[Object]]::New( ( 'A', 'r', 'r', 'a', 'y' ) ) # Is very, very fast.
$List = New-Object 'System.Collections.Generic.List[Object]'
'A', 'r', 'r', 'a', 'y' | ForEach-Object { $List.Add( $_ ) } # Is very , very slow. (Relatively)Can't you just cast a list as well? Not at PC to test:
[List[string]]@($arr)
Also your last example is slow because of ForEach-Object
Totally with you on avoiding New-Object, but mostly what you're illustrating here is bulk operations vs discrete. AddRange is almost as fast:
$Output | Add-Member -MemberType NoteProperty -Name 'AddRangeMethod' -Value (
Measure-Command {
$SlowlyCreatedGenericList2 = New-Object 'System.Collections.Generic.List[Byte]'
$SlowlyCreatedGenericList2.AddRange($Bytes)
}
)
$Output | Add-Member -MemberType NoteProperty -Name 'AddRangeList' -Value $SlowlyCreatedGenericList2
No, you are right. As shown here, [IO.File]::ReadAllLines does return a String[] object. The confusing error that you are seeing is explained in @mjolinor's answer (I won't repeat it here).
Instead, I will tell you how to fix the problem. To convert a String[] object into a List<String> object in PowerShell, you need to explicitly cast it to such:
PS > [string[]]$array = "A","B","C"
PS > $array.Gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String[] System.Array
PS >
PS > [Collections.Generic.List[String]]$lst = $array
PS > $lst.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True List`1 System.Object
PS >
In your specific case, the code would be:
$csvUserInfo = [IO.File]::ReadAllLines($script:EmailListCsvFile)
[Collections.Generic.List[String]]$x = $csvUserInfo
It does return [string[]], but that type doesn't have a tolist() method. I believe what you're seeing is the automatic member enumeration that was introduced in V3. V2 throws the same error, but for [System.String[]].
It looked for that method on the array, and didn't find it so it tried a member enumeration, to see if it was a method of the array members. It didn't find it there either, and that's where it gave up so you got the error on the array member object.
I can't get that constructor to work either. This however seems to work:
# $temp = Get-ResourceFiles
$resourceFiles = New-Object System.Collections.ArrayList($null)
$resourceFiles.AddRange($temp)
You can also pass an integer in the constructor to set an initial capacity.
What do you mean when you say you want to enumerate the files? Why can't you just filter the wanted values into a fresh array?
Edit:
It seems that you can use the array constructor like this:
$resourceFiles = New-Object System.Collections.ArrayList(,$someArray)
Note the comma. I believe what is happening is that when you call a .NET method, you always pass parameters as an array. PowerShell unpacks that array and passes it to the method as separate parameters. In this case, we don't want PowerShell to unpack the array; we want to pass the array as a single unit. Now, the comma operator creates arrays. So PowerShell unpacks the array, then we create the array again with the comma operator. I think that is what is going on.
Probably the shortest version:
[System.Collections.ArrayList]$someArray
It is also faster because it does not call relatively expensive New-Object.
You do not have to use a loop to perfom this, if you do not want to perform any operations on each IP.
Simply use
CopyWrite-Host ($whitelist -join "`n")
This will inject a new line between each element in the array. I have compared the differences below:
Copy$array = @('foo','bar')
write-host ($array -join "`n")
Output:
foo
bar
versus
Copy$array = @('foo','bar')
write-host $array
Output:
foo bar
Hope it helps.
CopyWrite-Host $elements -Separator "`n"
https://docs.microsoft.com/previous-versions/powershell/module/microsoft.powershell.utility/write-host?view=powershell-3.0