Arrays - Zero is the loneliest number

In simple terms an array is an organized list of variables. Arrays are organized by index, as objects are added to the list they are automatically given the next whole number as their index. Arrays in most languages, including Powershell, are zero-indexed, which means that the first object in the array is at index zero and it goes up from there.

Creating an Array

There is more than one way to create an array in Powershell, all of which are valid and have their own benefits. You can create an array using @() and assigning the result to a variable, or you can list out your array with a comma separator. If you just need to populate an array with a range of numbers, you can use the range operator ..

#Create an empty array using @()
$EmptyArray = @()

#Create an Array with four strings in it
$FourArray = @('Zero','One','Two','Three')

#Create an Array with a comma separator
$CommaArray = 'Zero','One','Two','Three'

#Create an Array containing the range from 56 to 78
$RangeArray = 56..78
A Demonstration of multiple ways to create an Array

Accessing an Array

Accessing an individual element of an array is done by using the variable name with the [] operator and the index.

#Create an Array with four strings in it
$Array = @('Zero','One','Two','Three')

#Output the 3rd Element of the Array
$Array[2]
A demonstration of accessing an individual element of an array

Note that accessing the third element of the array requires using the number 2. This is because the array is zero indexed. Using 3 would return the fourth element of the array, this is called an Off-By-One Error.

An aside about Off-by-One

An Off-by-one error occurs when you don't get the results you were expecting from a program because somewhere in it, a number you used is off by one of what would return the correct results. An Off-by-One error commonly happens in two places, one is array indexes, the other is in loops, which we'll talk about when we talk about loops. In this case, it happens because of the difference in how humans think about lists, and how the language indexes the array.

If you ask most people in the US to count something, they're going to start counting at one. When counting it makes sense to skip over using zero when we can obviously see that there are more objects than that. This is where Off-by-one errors happen in arrays.

The index in an array was not originally designed as a counter of the number of objects in an array. In BCPL (which was a precursor to the language C) the index was designed as an offset. When you declared an array, you had to declare what was contained in the array (Unlike Powershell, you couldn't mix types. An array contained only ints, or only floats, or so on). If you declared an int array, the system knew that an int was a certain size, and so reserved enough space in memory for the array based on the size of the declared array. To access each individual element of the array, the system looks in memory at the address of the array, plus an offset of the index multiplied by the size of the type of the array.

//Declare an array of Type Int with space for five values
int array[5] = {1,6,8,2,78};

//Access the 3rd element of the Array
array[2];

//Assume an int takes up 4 bytes of space. (This is true on most 32-bit and 64-bit systems. 
//Accessing the 3rd element of the array is done by going to the start of the array then moving by an offset of 2 * 4 bytes
//This is why Indexes start at zero, to access the first element of the array, you can't add any offset to the starting address.
A demonstration of declaring and accessing an array in C
A simple diagram of an array in memory. Four different colored purple bars representing each element of the array.
A simple diagram of an Array in memory

Thinking of the index as an offset rather than a count will help you from making too many Off-by-one errors, at least where arrays are involved. Back to arrays.

Accessing an Array - Part 2

There are a number of unique ways to access an array in Powershell when you want to access multiple elements, or perhaps work backwards through an array.

If you need a certain number of elements from the array, you can access them by separating them with a comma in between the [] operator.

#Declare an array
$Array = @(0..10)

#Access three items of an array
$Array[0,2,3]

#Duplicating an index will return that element twice
$Array[0,2,0]
A demonstration of accessing multiple elements of an array

You can also access multiple elements by using the range operator .. .

#Declare an array
$Array = @(0..10)

#Access the first five elements of an array
$Array[0..4]

#It also works in descending order, this will output the results in the opposite order as the first
$Array[4..0]
A demonstration of accessing multiple elements of an array using the range operator

If you need to access the last element of an array you can use some built in Powershell instance variables to do it ( .count ) or, you can use negative numbers.

#Declare an array
$Array = @(0..10)

#Access the last element using .count
$Array[$Array.Count-1]

#Access the last element using negative numbers 
$Array[-1]

#Access the last five elements of the array using .count and the range operator
$Array[($Array.Count-6)..($Array.Count-1)]

#Access the last five elements of the array using negative numbers and the range operator
$Array[-5..-1]
A demonstration of using the .count instance variable and negatives numbers to access the last element of an array.

You can see how using negative number results in much cleaner and easier to read code, provided you know how the negative numbers work with arrays. $Array.Count returns the count of elements in the array, not the last index, so you have to subtract one from the count to get the last index. Otherwise, you're going to have an Off-by-one error.

Updating an Array

Updating a single element in an array is easy, just set that element to it's new value using the assignment operator = . Trying to assign something to an index that is outside of the range of the array will result in an error however.

#Declare an array
$Array = @(0..10)

#Set the element at index 5 to 15
$Array[5] = 15

#Attempt to set the element at index 20 to 42. This will error
$Array[20] = 42
A demonstration of updating an array element

Adding new elements to an Array

You can't.

Once an array is declared, you can't change the size of it. But there are some work arounds for adding elements to an array.

If you have two arrays you can add them together using the + operator which creates a third array out of the first two.

#Declare two arrays
$ArrayOne = @(0,2)
$ArrayTwo = @(1,3)

#Create a third array from $ArrayOne and $ArrayTwo
$ArrayThree = $ArrayOne + $ArrayTwo
Combining two arrays into one

To add a single element to an array, you can use the += operator. However, this doesn't simply add a new element to the array, it creates a new array combining the original array and the new element.

#Declare an array
$Array = @(0,1,2,3)

#Add an element to the Array
$Array += 4
Adding an element to an array

While this works on a small dataset it doesn't scale well. Which brings us to Lists.

Lists

Think of a List as a really fancy array. They're dynamic so you can add and remove elements as needed but otherwise function exactly the same as an array does.

Creating a List

Lists are part of a special namespace in Powershell, so you have to tell it where to look to use them. You can do this each time you declare a list, or you can do it at the start of your script with a bit of code. Lists are required to have a type, so you have to define what is contained in the list when you declare it. If you need to store multiple types or objects, you can use PSObject as the type.

#Declaring a list with the full namespace
$List = [System.Collections.Generic.List[PSObject]]::new()

#Declaring a list using a separate namespace declaration. This only needs to be done once per script. These have to be the first lines in your script.
using namespace System.Collections.Generic

#Declaring a list having already declared the namespace
$list = [List[PSObject]]::new()

#Instead of using new, if you have your data, you can use the array call to fill the list initially.
$list = [List[PSObject]]@(1,5,"Bartholomew Jones",6.7)
A demonstration of declaring a List

Adding and Removing Elements from a List

Adding and removing elements from a list is done with the .add() and .remove() functions of the List. Adding an element always adds it to the end of the list. To remove an element, you have to pass that element in with .remove().

using namespace System.Collections.Generic

#Declare a list with multiple elements
$List = [List[PSObject]]@(0..10)

#Add a new element to the list
$List.Add(11)

#Remove an element from the list
$List.Remove($List[5])

#Display $list in Console
Write-Out $List
A demonstration of adding and removing elements to a List

Multi-Dimensional Arrays

One thing you may run into or find a use for is a Multi-Dimensional array or nested array. You can nest as many arrays inside of each other as you want, however they get really hard to conceptualize after three. An easy way to visualize a two dimensional array (that is two layers of arrays) is a tic tac toe board or an excel file, for a three dimensional array visualize a Rubik's Cube. These functions pretty much identical to a normal array, just with extra sets of [] for each dimension.

#Declare a Two Dimensional Array that represents a Tic Tac Toe board
$TicTacToe = @(@('_','_','_'),@('_','_','_'),@('_','_','_'))

#Change the middle square to X
$TicTacToe[1][1] = 'X'

#Change the upper left corner to O
$TicTacToe[0][0] = 'O'

#Change the upper right corner to X
$TicTacToe[0][2] = 'X'

#Note that if you output $TicTacToe it wont display like a Tic Tac Toe Board with out some extra formatting. Use this funcion below to output the board. If you don't understand everything happening below yet, that's okay. We'll get to that in Loops and Functions

#A function to output a two dimensional array as a Tic Tac Toe Board
function Write-TicTacToeBoard
{
    param($TicTacToeBoard)
	
    #Loop through each row and generate an output string
    foreach($item in $TicTacToeBoard)
    {
        $line = $item[0] + "|"+ $item[1] + "|" + $item[2]
        Write-Output $line
    }
}

Write-TicTacToeBoard -TicTacToeBoard $TicTacToe
Demonstrating declaring and using a Two Dimensional Array using Tic Tac Toe as an example.