Archive for January, 2018

Practical guide to PowerShell Arrays


Microsoft defines Arrays in PowerShell as ‘data structures designed to store collections of items’. This posting will go over practical aspects of arrays in PowerShell 5.1

An array is a data structure that holds an ordered list of items.

Array items are not unique (can have duplicates)

Array items are referred to as elements. Although array elements can be of different data types, in most practical use cases, they’re all of the same type.

Referencing array elements

Array elements are referred to by their order in the array where the first element in the array is order #0. They follow the array variable enclosed in square brackets [ ] as in:

In this example, $b[2] refers to the third element of the array which has the value of 4

The last element in an array is number -1, the element before last is -2, the 2nd element from the end is -3, …

We can reference more than one element of an array by using the comma (,), the range operator (..), or the plus sign (+)

In this array $c we have 10 random numbers each is between 0 and 9:

The comma ‘,’ is used to separate multiple array elements. For example to reference the 3rd and 6th elements of the array we use $c[2,5] and we expect to get 1 and 4 values in this example.

The range operator ‘..’ is used to get a sequential range of elements. To get elements 4 through 7 we can use $c[3..6]

The range operator circles the array boundary. For example, $c[-2..2] will list the element before last [-2], the last element [-1], the first element [0], the second element [1], and the third element [2] in this order

Although not very practical, you can mix individual elements and ranges when referencing array elements. Ranges are to be preceded by the plus sign (+). Individual elements are to be preceded by the comma (,) except after a range where they must be preceded by the plus sign (+) as well:

What we see often in practical use of arrays:

  • Referencing one or more array elements separated by commas as $c[1,4,6]
  • Referencing a sequential range of array elements as $c[3..5]
  • Referencing the last array element $c[-1]
  • We often don’t know how many elements an array has. We can find that out by using $c.Count or $c.Length or $c.GetUpperBound(0)+1 or $c.GetLength(0). In our example, array $c has 10 elements.

Declaring a variable as an array

There are several ways to declare an array. The standard/default way is to let PowerShell figure it out.

In this example, PowerShell figured out that $d should be an array based on the data that we’re trying to store in it.

We can specify that by using the array sub-expression operator @(). This is particularly useful when we want to make sure a variable is an array

Take this practical example:

Get-Service Win* | foreach {
    if ($PSItem.Status -eq 'Running') { $myList +=$PSItem }
}
$myList

We get errors on the last 3 cycles of this 4 cycle iteration:

The reason is that on cycle 1 of this 4 cycle iteration, PowerShell auto-declared the variable $myList as a ‘System.ComponentModel.Component’ which is the object returned from the Get-Service cmdlet. We actually want $myList to be an array of such objects that can hold 0, 1, or more items. There are 2 ways to do this:

  1. Declare $myList as an array upfront using the array sub-expression operator @()

    or
  2. Assign the resulting collection of the 4 cycle loop in one step instead of assigning each found value at the end of the if statement

The third way to declare an array is to type cast it. This only works in cases where all elements of the array are of the same data type. For example:

or

Here $myIPList is an array of objects, each is of data type ‘System.Net.IPAddress’

Removing array duplicates

Removing array duplicates is a simple matter of using ‘select -unique‘ as in:

Finding one or more values in an array

Finding a single value is a simple matter of using -match or -eq operators. For example:

In this example, the 8th and 10th elements have the value 3

Finding elements in array $a that also exist in array $b (AKA intersection of arrays $a and $b) can be done using ‘where’ statement and -in operator

This method produces the same result if the array elements are unique and sorted as in:

($d | select -Unique | where { $PSItem -in ($c | select -Unique) } | sort ) -join ', '
($c | select -Unique | where { $PSItem -in ($d | select -Unique) } | sort ) -join ', '

Adding/Removing array elements

Most arrays are of fixed length, which means the .add(), .remove(), .RemoveAt(), and the .Insert() array methods will fail.

To add element to an array, we recreate/reassign values to the array. For example, to add value 17 at the end of the $c array:

This makes 17 the 11th element of the $c array

To insert 17 as the 3rd element of the $c array we rebuild $c as in:

$c = $c[0,1] + 17 + $c[2..9]

This inserts 17 value as the 3rd element in the $c array

To remove a value from an array we can use the -notmatch operator. Note that this will remove all values that match. For example this removes the 17 value:

$c = $c -notmatch 17 

This example removes all 3 values:

$c = $c -notmatch 3

Whereas this example removes the first 3 value only:

$c = 0..($c.Length-1) | foreach { if ($PSItem -ne $c.IndexOf(3)) { $c[$PSItem] } }

This example uses the array object .IndexOf() method to determine the first occurrence of the element with the 3 value. It then loops through the array elements using foreach and compiles a new array skipping the 8th element identified by $c.IndexOf(3), and reassigns this new array to variable $c

To remove the 4th and 8th array elements for example regardless of their values, we use an expression similar to:

$c = 0..($c.Length-1) | foreach { if ($PSItem -notin 3,7) { $c[$PSItem] } }

Multi-dimensional arrays

Multi-dimensional arrays are rarely used and can be constructed using .NET as in:

$2D = [int[,]]::new(3,4)

This creates a 2 dimensional array. The first dimension 0 is 3 elements long, numbered 0..2 and the second dimension 1 is 4 elements long numbered 0..3

This sample code shows how to identify the count of array dimensions and the length of each dimension:

$2D = [int[,]]::new(3,4)
"2D is of type '$($2D.GetType().BaseType)'"
"It has '$($2D.Rank)' dimensions"
1..$2D.Rank | foreach { "Dimension '$($PSItem-1)' has '$($2D.GetUpperBound($PSItem-1))' elements" }

This example shows how to populate and reference multi-dimensional arrays:

"Example populating and referencing 2D:"
$i = 0
foreach ($Dim1 in 0..$2D.GetUpperBound(0)) { 
    foreach ($Dim2 in 0..$2D.GetUpperBound(1)) {
       $2D[$Dim1,$Dim2] = $i
       $i++
    }
}
foreach ($Dim1 in 0..$2D.GetUpperBound(0)) { 
    foreach ($Dim2 in 0..$2D.GetUpperBound(1)) {
       "2D[$Dim1,$Dim2] = $($2D[$Dim1,$Dim2])"
    }
}

 

Advertisements