Today I want to talk a little about multidimensional arrays in Windows Powershell. Multidimensional arrays are one of the complex data types supported by Powershell. They can be used to dynamically store information in a volatile table without having it written to a real database in a file on the disk.
In that sense multidimensional arrays extend a lot the functionalities of simple arrays, which are oriented to storing just series of polymorphic values such as:
- Strings: ‘Apples’, ‘Peaches’, ‘Oranges’, ‘Apricots’
- Integers: 1,2,3,4,5
- ... or a mix of any kind of value: ‘Sergio’,’Leone’,’January’,3,1929,’Rome’
In fact you can imagine a multidimensional array like a table, with columns and rows, where each cell has its own index (i.e. [7,5]).
To make things clear, let’s set-up a multidimensional array and let’s see how it can be used, for instance, to manage our employees coming and going.
First let’s do some cleaning on our screen and let’s define a $emp_counter variable which we can later use to retrieve information about employees:
- clear-host
- write-host "==========STARTED=============="
- $emp_counter = $null # Empty key to track our employees
Ok, the game commences here. We define here the desired array as a dynamic array. We go for the following syntax:
- $employee_list = @() # Dynamic array definition
This will result in an empty array named $employee_list. The drawback of an empty array such this is that each time you will perform an operation on it, such as adding a row, or just adding a single value, Windows Powershell will have to rebuild (by making a copy) the whole array to follow its structural modifications. And of course this would have a high impact on the performance of your script if thousands or even millions of operations are performed. Nonetheless an array with no predefined size will give us a greater flexibility. That’s why we call it a dynamic array in the end, or a jagged array to be more precise.
We could also have start by defining a fixed size array (which is called a true multidimensional array) but in our example we don’t want to stick to a square matrix so we will not use the following command (but you are free to do so if you want to deeper explore arrays in Powershell):
- $employee_list = New-Object 'object[,]' 4,5 # Alternative method to create arrays
- write-host "==============================="
- write-host "Checking array information"
- write-host "==============================="
- $employee_list.gettype() # Check array information
IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.ArrayNow let’s add four rows, one for each of our new IT administrators: Tommy, Jonathan, Mario and Jeremy.
- $emp_counter ++ # We increase the counter by one
- $employee_list += ,@($emp_counter, 'Tommy',76000, 'Sysadm','Windows')
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Jonathan',80000, 'Sysadm','Unix') #
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Mario',64000,'DBA','Oracle') #
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Jeremy',70000, 'DBA','MS SQL') #
- write-host "==============================="
- write-host "Array filled with four rows"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee) # List array content using foreach()
- }
1 Tommy 76000 Sysadm Windows 2 Jonathan 80000 Sysadm Unix 3 Mario 64000 DBA Oracle 4 Jeremy 70000 DBA MS SQLEach time we put a comma, we are like telling Powershell to start a new row in the multidimensional array. Here’s a graphical representation:
As you can see, for the moment this jagged array resembles a square matrix with four rows and five columns. The first column is our subjective index, the second is the name, the third is the salary, the fourth is the role, and the fifth is the specialization.
This was the easy part. Now let’s have a look at the different operations that can be performed on this scaring polymorphic multidimensional array. In particular, let’s see how to:
- Add a row to the jagged array
- Add a row with a non standard number of values
- Delete a row from the jagged array
- Sort the content of the array by one known column
Let’s start with adding a full grown-up row for our new networking specialist, Bill:
- $employee_list += ,@($emp_counter,'Bill',81000,'Network','Cisco')
- write-host "==============================="
- write-host "Bill is hired"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
1 Tommy 76000 Sysadm Windows 2 Jonathan 80000 Sysadm Unix 3 Mario 64000 DBA Oracle 4 Jeremy 70000 DBA MS SQL 5 Bill 81000 Network CiscoOr graphically:
Now let’s see how to add a row with a non standard number of values. For instance, our new employee Cedric has skills in both Windows and NAS worlds, so we will have six columns instead of five like for the others employees. This will change our array from a true multidimensional array to a jagged array (for more details on the difference between jagged arrays and true multidimensional arrays check this cool post.)
We would add Cedric to our array this way:
- $employee_list += ,@($emp_counter,'Cedric',75000,'Sysadm','Windows','Netapp')
- write-host "==============================="
- write-host "Cedric Is hired"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
1 Tommy 76000 Sysadm Windows 2 Jonathan 80000 Sysadm Unix 3 Mario 64000 DBA Oracle 4 Jeremy 70000 DBA MS SQL 5 Bill 81000 Network Cisco 6 Cedric 75000 Sysadm Windows NetappOr to be more clear:
As you can see, the array has extended to include one more column and its form is now irregular.
Now let’s suppose that our firm is facing an economical crisis and we, the good managers, want to reduce our costs and keep earning more $$$. We could fire Tommy because Cedric is more convenient due to the fact that he has two competences: Windows and NAS. So, let’s have a look at how to delete the row for Tommy from our multidimensional array. As far as I know, no nice easy way to do this. We must go through a foreach() cycle and then overwrite the original array with all the rows except Tommy’s one, which we will have found and excluded using the classical -notmatch operator against field [1] :
- $employee_list_temp = @()
- Foreach($employee2 in $employee_list)
- {
- If($employee2[1] –notmatch "Tommy") # Index 1 is for the employee’s name
- {
- $employee_list_temp += ,($employee2) # ... bye-bye Tommy
- }
- }
- $employee_list = $employee_list_temp # Updating array content
- write-host "==============================="
- write-host "Tommy has left… ;-)"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
2 Jonathan 80000 Sysadm Unix 3 Mario 64000 DBA Oracle 4 Jeremy 70000 DBA MS SQL 5 Bill 81000 Network Cisco 6 Cedric 75000 Sysadm Windows NetappAs you can see the row for Tommy has magically disappeared. The array first row is Jonathan’s one now.
The last action we want to be able to perform is to sort the content of out multidimensional array. To do so, nothing better than the classical sort-object cmdlet using field number 2, which is the salary in ascending order in our case:
- $employee_list_by_wage = $employee_list | sort-object @{Expression={$_[2]}; Ascending=$true}
- write-host "==============================="
- write-host "Employees by salary"
- write-host "==============================="
- foreach($employee3 in $employee_list_by_wage)
- {
- Write-host ($employee3)
- }
- write-host "==========DONE================"
3 Mario 64000 DBA Oracle 4 Jeremy 70000 DBA MS SQL 6 Cedric 75000 Sysadm Windows Netapp 2 Jonathan 80000 Sysadm Unix 5 Bill 81000 Network CiscoHere’s the graphical version:
Ok guys, now you know the basics of multidimensional array handling in Powershell and you know how to add and remove elements from jagged arrays. Of course this script can be optimized, reorganized, compacted, but this is out of my scope. I just wanted to quickly demonstrate the ease with which Powershell manages data. I hope that you see my point of view. Powershellers, if you want to suggest any improvements or correction, you are warmly welcome to do so!
Here's the full code that you can run at once:
- clear-host
- write-host "==========STARTED=============="
- $emp_counter = $null # Empty key to track our employees
- $employee_list = @() # Dynamic array definition
- write-host "==============================="
- write-host "Checking array information"
- write-host "==============================="
- $employee_list.gettype() # Check array information
- $emp_counter ++ # We increase the counter by one
- $employee_list += ,@($emp_counter, 'Tommy',76000, 'Sysadm','Windows')
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Jonathan',80000, 'Sysadm','Unix')
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Mario',64000,'DBA','Oracle')
- $emp_counter ++
- $employee_list += ,@($emp_counter, 'Jeremy',70000, 'DBA','MS SQL')
- write-host "==============================="
- write-host "Array filled with four rows"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee) # List array content using foreach()
- }
- $emp_counter ++ # We increase the counter by one
- $employee_list += ,@($emp_counter,'Bill',81000,'Network','Cisco')
- write-host "==============================="
- write-host "Bill is hired"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
- $emp_counter ++ # We increase the counter by one
- $employee_list += ,@($emp_counter,'Cedric',75000,'Sysadm','Windows','Netapp')
- write-host "==============================="
- write-host "Cedric Is hired"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
- $employee_list_temp = @()
- Foreach($employee2 in $employee_list)
- {
- If($employee2[1] –notmatch "Tommy") # Index 1 is for the employee’s name
- {
- $employee_list_temp += ,($employee2) # ... bye-bye Tommy
- }
- }
- $employee_list = $employee_list_temp # Updating array content
- write-host "==============================="
- write-host "Tommy has left… ;-)"
- write-host "==============================="
- foreach($employee in $employee_list)
- {
- Write-host ($employee)
- }
- $employee_list_by_wage = $employee_list | sort-object @{Expression={$_[2]}; Ascending=$true}
- write-host "==============================="
- write-host "Employees by salary"
- write-host "==============================="
- foreach($employee3 in $employee_list_by_wage)
- {
- Write-host ($employee3)
- }
- write-host "==========DONE================"
Thanks for the post, was looking for something clean on multidimensional arrays.
ReplyDeleteYou just forgot to increment emp_counter before adding Bill and Cendric. They both showed up as "4" like Jeremy.
This popped up at the top of my Google search and deservedly so. *Very* well done. Thanks so much for a clear and concise summary of the subject.
ReplyDeleteVery good post, thank you. It's very useful.
ReplyDeleteThank you for that post.
ReplyDeleteNice post. We end up with 3 people who have the emp_counter set a 4 because you didn't continue to increase it each time, but very very helpful indeed.
ReplyDeleteThanks for the hint! I corrected the final code by adding
ReplyDelete$emp_counter ++ # We increase the counter by one
when we add Bill and Cedric.
Thanks fro the feedback
Excellent: easy and clear.
ReplyDelete:)
Great example with clear explanation
ReplyDeleteExcellent; Solved my problems...
ReplyDeleteHow do you select a column from that table and display just 1 column? Say you want to just see a list of employee names.
ReplyDeleteSince there's no column name, i'm having a little trouble with that.
Great and clear article, very helpful.
ReplyDeleteClear explanation! Very helpful. Thank you
ReplyDeleteThanks for this clear explanation. Great work!
ReplyDeletePerfect!
ReplyDeleteGreat article but could do with knowing how to change a value in an array, i.e. if someone was to get a payrise how would you change the values
ReplyDeleteThis really helped me. Thank you so much!
ReplyDeleteYou could greatly increase the performance in this as well as allowing to delete a "row" of the array quite simply by using ArrayList or the proper generic.
ReplyDeletehttps://powershell.org/2013/09/16/powershell-performance-the-operator-and-when-to-avoid-it/
https://foxdeploy.com/2016/03/23/coding-for-speed/
beautifull now
ReplyDelete