44

I have a way of doing Arrays in other languagues like this:

$x = "David"
$arr = @()

$arr[$x]["TSHIRTS"]["SIZE"] = "M"

This generates an error.

Zombo
  • 1
  • 62
  • 391
  • 407
basickarl
  • 37,187
  • 64
  • 214
  • 335
  • 1
    As was pointed out earlier, the code that you have posted is a dictionary or a hash, not a common array. – EBGreen Feb 22 '12 at 15:53

11 Answers11

66

You are trying to create an associative array (hash). Try out the following sequence of commands

$arr=@{}
$arr["david"] = @{}
$arr["david"]["TSHIRTS"] = @{}    
$arr["david"]["TSHIRTS"]["SIZE"] ="M"
$arr.david.tshirts.size

Note the difference between hashes and arrays

$a = @{} # hash
$a = @() # array

Arrays can only have non-negative integers as indexes

Zombo
  • 1
  • 62
  • 391
  • 407
manojlds
  • 290,304
  • 63
  • 469
  • 417
33

from powershell.com:

PowerShell supports two types of multi-dimensional arrays: jagged arrays and true multidimensional arrays.

Jagged arrays are normal PowerShell arrays that store arrays as elements. This is very cost-effective storage because dimensions can be of different size:

$array1 = 1,2,(1,2,3),3
$array1[0]
$array1[1]
$array1[2]
$array1[2][0]
$array1[2][1]

True multi-dimensional arrays always resemble a square matrix. To create such an array, you will need to access .NET. The next line creates a two-dimensional array with 10 and 20 elements resembling a 10x20 matrix:

$array2 = New-Object 'object[,]' 10,20
$array2[4,8] = 'Hello'
$array2[9,16] = 'Test'
$array2

for a 3-dimensioanl array 10*20*10

$array3 = New-Object 'object[,,]' 10,20,10
CB.
  • 58,865
  • 9
  • 159
  • 159
  • 5
    You don't necessarily have to access .NET: [PS] C:\>$array = @(,@(,@(,@()))) [PS] C:\>$array[0][0][0] = 1 – mjolinor Feb 22 '12 at 15:22
  • 2
    They aren't quite the same thing, anyway. One has to be referenced as $array[n,n], the other as $array[n][n]. – mjolinor Feb 22 '12 at 15:33
  • 1
    With .NET it is also easier to pre-generate the dimension sizes. With the PS method mentioned above you would have to pre-generate it yourself or use the add operators to add new members. You can't simply write `$array[0]0][1] = 2` as that is out of bounds of the array. While `$array[1][0][0] = 2` would be indexing a null array. Which if you want a random access matrix that is not ideal. – New Guy Sep 03 '15 at 22:43
  • the .net thing also adds coherence... if you define a Byte array... and you try to store some other type... Powershell will 'shout' to you!!! ;-) – ZEE Apr 26 '19 at 21:49
  • 2
    For anyone who wants to use multidimensional arrays, you would use the [`GetLength`](https://learn.microsoft.com/en-us/dotnet/api/system.array.getlength?view=net-5.0) method to get the number of elements for a specific dimension. For example, `$array2.GetLength(0)` would return 10 for `$array2 = New-Object 'object[,]' 10,20` and `$array2.GetLength(1)` would return 20. – Dave F Feb 05 '21 at 02:13
  • 2
    @DaveF and `$array2.GetUpperBound(n)` to find the length in terms of its index number, which is what you need when looping over the array. It's equivalent to `$array.GetLength(n) - 1`. This method can incidentally also be used for normal PS arrays, where you simply enter a 0 for n, as it's one-dimensional. – Blaisem Jan 13 '22 at 14:25
  • 3
    In PS Core 7.x you can use a more concise syntax: `$array = [object[,]]::new(10, 20)` – zett42 Apr 06 '22 at 19:09
  • 1
    @zett42 That's not new to PowerShell 7, it works fine in PowerShell 5 as well. – PMental Dec 08 '22 at 13:43
17

To extend on what manojlds said above is that you can nest Hashtables. It may not be a true multi-dimensional array but give you some ideas about how to structure the data. An example:

$hash = @{}

$computers | %{
    $hash.Add(($_.Name),(@{
        "Status" = ($_.Status)
        "Date"   = ($_.Date)
    }))
}

What's cool about this is that you can reference things like:

($hash."Name1").Status

Also, it is far faster than arrays for finding stuff. I use this to compare data rather than use matching in Arrays.

$hash.ContainsKey("Name1")
ggorlen
  • 44,755
  • 7
  • 76
  • 106
alistek
  • 401
  • 2
  • 4
14

Knowing that PowerShell pipes objects between cmdlets, it is more common in PowerShell to use an array of PSCustomObjects:

$arr = @(
    [PSCustomObject]@{Name = 'David';  Article = 'TShirt'; Size = 'M'}
    [PSCustomObject]@{Name = 'Eduard'; Article = 'Trouwsers'; Size = 'S'}
)

Or for older PowerShell Versions (PSv2):

$arr = @(
    New-Object PSObject -Property @{Name = 'David';  Article = 'TShirt'; Size = 'M'}
    New-Object PSObject -Property @{Name = 'Eduard'; Article = 'Trouwsers'; Size = 'S'}
)

And grep your selection like:

$arr | Where {$_.Name -eq 'David' -and $_.Article -eq 'TShirt'} | Select Size

Or in newer PowerShell (Core) versions:

$arr | Where Name -eq 'David' | Where Article -eq 'TShirt' | Select Size

Or (just get the size):

$arr.Where{$_.Name -eq 'David' -and $_.Article -eq 'TShirt'}.Size

Addendum 2020-07-13

Syntax and readability

As mentioned in the comments, using an array of custom objects is straighter and saves typing, if you like to exhaust this further you might even use the ConvertForm-Csv (or the Import-Csv) cmdlet for building the array:

$arr = ConvertFrom-Csv @'
Name,Article,Size
David,TShirt,M
Eduard,Trouwsers,S
'@

Or more readable:

$arr = ConvertFrom-Csv @'
Name,   Article,   Size
David,  TShirt,    M
Eduard, Trouwsers, S
'@

Note: values that contain spaces or special characters need to be double quoted

Or use an external cmdlet like ConvertFrom-SourceTable which reads fixed width table formats:

$arr = ConvertFrom-SourceTable '
Name   Article   Size
David  TShirt    M
Eduard Trouwsers S
'

Indexing

The disadvantage of using an array of custom objects is that it is slower than a hash table which uses a binary search algorithm.
Note that the advantage of using an array of custom objects is that can easily search for anything else e.g. everybody that wears a TShirt with size M:

$arr | Where Article -eq 'TShirt' | Where Size -eq 'M' | Select Name

To build an binary search index from the array of objects:

$h = @{}
$arr | ForEach-Object {
    If (!$h.ContainsKey($_.Name)) { $h[$_.Name] = @{} }
    If (!$h[$_.Name].ContainsKey($_.Article)) { $h[$_.Name][$_.Article] = @{} }
    $h[$_.Name][$_.Article] = $_ # Or: $h[$_.Name][$_.Article]['Size'] = $_.Size
}

$h.david.tshirt.size
M

Note: referencing a hash table key that doesn't exist in Set-StrictMode will cause an error:

Set-StrictMode -Version 2
$h.John.tshirt.size
PropertyNotFoundException: The property 'John' cannot be found on this object. Verify that the property exists.
Community
  • 1
  • 1
iRon
  • 20,463
  • 10
  • 53
  • 79
8

Here is a simple multidimensional array of strings.

$psarray = @(
 ('Line' ,'One' ),
 ('Line' ,'Two')
)

foreach($item in $psarray)
{
    $item[0]
    $item[1]
}

Output:

Line
One
Line
Two
Xavier John
  • 8,474
  • 3
  • 37
  • 51
3

Another thread pointed here about how to add to a multidimensional array in Powershell. I don't know if there is some reason not to use this method, but it worked for my purposes.

$array = @()
$array += ,@( "1", "test1","a" )
$array += ,@( "2", "test2", "b" )
$array += ,@( "3", "test3", "c" )
FHC
  • 71
  • 7
2

Two-dimensional arrays can be defined this way too as jagged array:

$array =  New-Object system.Array[][] 5,5

This has the nice feature that

$array[0]

outputs a one-dimensional array, containing $array[0][0] to $array[0][4]. Depending on your situation you might prefer it over $array = New-Object 'object[,]' 5,5. (I would have commented to CB above, but stackoverflow does not let me yet)

Joachim Otahal
  • 272
  • 2
  • 9
1

you could also uses System.Collections.ArrayList to make a and array of arrays or whatever you want. Here is an example:

$resultsArray= New-Object System.Collections.ArrayList 
[void] $resultsArray.Add(@(@('$hello'),2,0,0,0,0,0,0,1,1))
[void] $resultsArray.Add(@(@('$test', '$testagain'),3,0,0,1,0,0,0,1,2))
[void] $resultsArray.Add("ERROR")
[void] $resultsArray.Add(@(@('$var', '$result'),5,1,1,0,1,1,0,2,3))
[void] $resultsArray.Add(@(@('$num', '$number'),3,0,0,0,0,0,1,1,2))

One problem, if you would call it a problem, you cannot set a limit. Also, you need to use [void] or the script will get mad.

Franco Pettigrosso
  • 4,056
  • 2
  • 20
  • 32
1

Using the .net syntax (like CB pointed above)
you also add coherence to your 'tabular' array...

if you define a array...
and you try to store diferent types
Powershell will 'alert' you:

$a = New-Object 'byte[,]' 4,4
$a[0,0] = 111;    // OK
$a[0,1] = 1111;   // Error

Of course Powershell will 'help' you
in the obvious conversions:

$a = New-Object 'string[,]' 2,2
$a[0,0] = "1111";   // OK
$a[0,1] = 111;      // OK also
ZEE
  • 2,931
  • 5
  • 35
  • 47
1

Im found pretty cool solvation for making arrays in array.

$GroupArray = @()
foreach ( $Array in $ArrayList ){
    $GroupArray += @($Array , $null)
}
$GroupArray = $GroupArray | Where-Object {$_ -ne $null}
  • This is pretty hard to follow. What's `$ArrayList` defined as? It seems pretty awkward to have to append nulls in there, then filter them out. What if the array has nulls in it? – ggorlen Mar 29 '23 at 02:44
0

Lent from above:

$arr = ConvertFrom-Csv @'
Name,Article,Size
David,TShirt,M
Eduard,Trouwsers,S
'@

Print the $arr:

$arr

Name   Article   Size
----   -------   ----
David  TShirt    M
Eduard Trouwsers S

Now select 'David'

$arr.Where({$_.Name -eq "david"})

Name  Article Size
----  ------- ----
David TShirt  M

Now if you want to know the Size of 'David'

$arr.Where({$_.Name -eq "david"}).size

M
Bart
  • 1