The helpful comments from Santiago Squarzon already explained the basic problem, the re-assignment to the variable $collection
.
In PowerShell though, most of the time you don't need to explicitly create and add to arrays because PowerShell automatically creates an array when you capture output of a function or a statement into a variable. This is also much more efficient because PowerShell has to recreate an array every time you use +=
(arrays are actually of fixed size!). When PowerShell automatically creates an array from captured output, it uses a more efficient data structure under the hood.
So I would modify the function like this:
function Get-InterfaceRoutes {
$interfaceIPs = Get-NetIPConfiguration | Select-Object -Property IPv4Address, InterfaceIndex
Foreach ($interfaceIP in $interfaceIPs) {
$route = Get-NetRoute -InterfaceIndex ($interfaceIP.InterfaceIndex) | Select-Object -Property ifINdex, DestinationPrefix, NextHop, RouteMetric, ifMetric
# Implicit output!
[PSCustomObject]@{
Index = ($interfaceIp.InterfaceIndex)
Address = ($interfaceIP.IPv4Address)
DestinationPrefix = ($route.DestinationPrefix)
NextHop = ($route.NextHop)
}
}
}
Note that I have removed the $collection
variable alltogether!
Instead, the [PSCustomObject]@{}
is implicit output of the function. PowerShell writes it to the success stream, from where it can be captured by the caller or simply let it pass through to display it in the console.
Now when you actually want to collect the output of that function into an array variable, you simply assign it to the variable.
$routesArray = @(Get-InterfaceRoutes)
"Collection of items:"
$routesArray | Format-List
The @()
array subexpression operator makes sure that you always create an array, even if the function outputs only a single object.
That being said, there are cases where you actually want to collect intermediate output in an array variable. Say you wanted to store the output from the foreach
in a local variable for further processing. In this case you still don't need to explicitly create an array, just assign from the foreach
statement to a variable:
# [array] is an alternative to @() for ensuring an array
[array] $collection = Foreach ($interfaceIP in $interfaceIPs) {
$route = Get-NetRoute -InterfaceIndex ($interfaceIP.InterfaceIndex) | Select-Object -Property ifINdex, DestinationPrefix, NextHop, RouteMetric, ifMetric
# Implicit output, will be captured and added to $collection
[PSCustomObject]@{
Index = ($interfaceIp.InterfaceIndex)
Address = ($interfaceIP.IPv4Address)
DestinationPrefix = ($route.DestinationPrefix)
NextHop = ($route.NextHop)
}
}
Finally, if you really really want to explicitly create an array (e. g. when you need to add to two arrays from one loop), I recommend to use the List
class, which is more efficient than a raw array for repeated additions. In addition it supports all operations of a raw array, e. g. you can use the index operator []
to access its elements.
# Create an instance of List, with elements of type Object.
$collection = [Collections.Generic.List[Object]]::new()
Foreach ($interfaceIP in $interfaceIPs) {
$route = Get-NetRoute -InterfaceIndex ($interfaceIP.InterfaceIndex) | Select-Object -Property ifINdex, DestinationPrefix, NextHop, RouteMetric, ifMetric
# Add a custom object to the list
$collection.Add( [PSCustomObject]@{
Index = ($interfaceIp.InterfaceIndex)
Address = ($interfaceIP.IPv4Address)
DestinationPrefix = ($route.DestinationPrefix)
NextHop = ($route.NextHop)
})
}