Related
I have a way of doing Arrays in other languagues like this:
$x = "David"
$arr = #()
$arr[$x]["TSHIRTS"]["SIZE"] = "M"
This generates an error.
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
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
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")
Hope some of that helps!
-Adam
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.
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
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)
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.
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
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" )
Im found pretty cool solvation for making arrays in array.
$GroupArray = #()
foreach ( $Array in $ArrayList ){
$GroupArray += #($Array , $null)
}
$GroupArray = $GroupArray | Where-Object {$_ -ne $null}
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
I have 2 dimension array. I would like to get each value of array.
I tried this, but each array in $Name has all the array in $Date. But my expectation first item in $Name equal to first item in $Date.
This is my expectation result:
A\11
B\12
C\13
$Name = #("A", "B", "C") #the size of this array not fixed
$Date = #("11", "12", "13") #the size of this array not fixed
foreach ($na in $Name)
{
foreach ($dt in $Date)
{
$NameAndDate = "$na\$dt"
Write-Host $NameAndDate
}
}
but from the code above I got this result
A\11
A\12
A\13
B\11
B\12
B\13
C\11
C\12
C\13
Anyone can help, please. Thank you
When combining two arrays, both having a non-fixed number of elements, you need to make sure you do not index beyond any of the arrays indices.
The easiest thing here is to use an indexed loop, where you set the maximum number of iterations to the minimum number of array items:
$Name = "A", "B", "C" # the size of this array not fixed
$Date = "11", "12", "13" # the size of this array not fixed
# make sure you do not index beyond any of the array indices
$maxLoop = [math]::Min($Name.Count, $Date.Count)
for ($i = 0; $i -lt $maxLoop; $i++) {
# output the combined values
'{0}\{1}' -f $Name[$i], $Date[$i]
}
Output:
A\11
B\12
C\13
Try this
$Name = #("A", "B", "C") #the size of this array not fixed
$Date = #("11", "12", "13") #the size of this array not fixed
foreach ($i in 0..($Name.Count -1)) {
write-host ($Name[$i] + "\" + $Date[$i])
}
I encountered some issue in converting my existing vbs script to PowerShell script. I have illustrate here with some dummy codes instead of my original code. In example 1, I only have 1 set of elements in the array, upon return the array variable to the function, it will only display P.
However in example 2, where I have 2 set of elements in the array, upon return the array variable to the function, it will display the elements properly.
If you print the array inside the function in example 1 and 2. There isn't any issue in getting the results.
I have googled and not able to find any solution to it. Many thanks in advance for the kind help.
Example 1:
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return $array1
}
$array2 = testArray
Write-Host $array2[0][1]
Result is "P".
Example 2:
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
$array1 += ,#("Orange","Pineapple")
return $array1
}
$array2 = testArray
Write-Host $array2[0][0]
Result is "Apple".
PowerShell unrolls arrays returned from a function. Prepend the returned array with the comma operator (,, unary array construction operator) to wrap it in another array, which is unrolled on return, leaving the nested array intact.
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return ,$array1
}
when you declare single line array like
$array1 = "Apple","Banana"
when you call :
$array1[0][1]
this will happen :
this code
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return $array1
}
$array2 = testArray
Write-Host $array2[0][1]
exact the same of this:
$array1 = "Apple","Banana"
but when you declare 2 row of array like :
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
$array1 += ,#("Orange","Pineapple")
return $array1
}
$array2 = testArray
Write-Host $array2[0][0]
this will happen :
if you need apple in your first code just call array[0] not array[0][0]. array[0][0] return char for you.
sorry for my bad english i hope you understand
$array1 inside your function TestArray is always of rank 1, fixed, and has never an element with type array. Check how a + is defined on arrays.
In this case you want to use an ArrayList:
$array1=[Collections.ArrayList]::new(10) # 10 is capacity
After that you can use the Add function. Even with arrays. And this time they will not be flattened, as was the case with the + array operator.
Other dynamic collection types also exist. But this one looks ok for many purposes.
I encountered some issue in converting my existing vbs script to PowerShell script. I have illustrate here with some dummy codes instead of my original code. In example 1, I only have 1 set of elements in the array, upon return the array variable to the function, it will only display P.
However in example 2, where I have 2 set of elements in the array, upon return the array variable to the function, it will display the elements properly.
If you print the array inside the function in example 1 and 2. There isn't any issue in getting the results.
I have googled and not able to find any solution to it. Many thanks in advance for the kind help.
Example 1:
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return $array1
}
$array2 = testArray
Write-Host $array2[0][1]
Result is "P".
Example 2:
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
$array1 += ,#("Orange","Pineapple")
return $array1
}
$array2 = testArray
Write-Host $array2[0][0]
Result is "Apple".
PowerShell unrolls arrays returned from a function. Prepend the returned array with the comma operator (,, unary array construction operator) to wrap it in another array, which is unrolled on return, leaving the nested array intact.
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return ,$array1
}
when you declare single line array like
$array1 = "Apple","Banana"
when you call :
$array1[0][1]
this will happen :
this code
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
return $array1
}
$array2 = testArray
Write-Host $array2[0][1]
exact the same of this:
$array1 = "Apple","Banana"
but when you declare 2 row of array like :
function testArray {
$array1 = #()
$array1 += ,#("Apple","Banana")
$array1 += ,#("Orange","Pineapple")
return $array1
}
$array2 = testArray
Write-Host $array2[0][0]
this will happen :
if you need apple in your first code just call array[0] not array[0][0]. array[0][0] return char for you.
sorry for my bad english i hope you understand
$array1 inside your function TestArray is always of rank 1, fixed, and has never an element with type array. Check how a + is defined on arrays.
In this case you want to use an ArrayList:
$array1=[Collections.ArrayList]::new(10) # 10 is capacity
After that you can use the Add function. Even with arrays. And this time they will not be flattened, as was the case with the + array operator.
Other dynamic collection types also exist. But this one looks ok for many purposes.
Is it possible to compare two arrays and remove the values that are equal (if they are at the same index), without iterating through both arrays? Here is an example:
$array1 = #(1,2,3,4,5,6,7,23,44)
$array2 = #(1,1,3,4,5,7,6,23,45)
$array3 = $sudo_compare_function $array1 $array2
where $array3 would now contain an array of indexes where $array2 is different from $array1 array:
(1,5,6,8)
If there isn't something like this, is there an easy way to do something similar without iterating through both arrays?
Use the Compare-Object cmdlet to get an array of differing values:
$array1 = #(1,2,3,4,5,6,7,23,44)
$array2 = #(1,1,3,4,5,7,6,23,45)
$array3 = #(Compare-Object $array1 $array2 | select -Expand InputObject
For comparing just the corresponding indexes you'll have to manually make the comparison:
function Compare-Indexes($a1, $a2) {
$minindex = [math]::Min($a1.Length, $a2.Length)
$maxindex = [math]::Max($a1.Length, $a2.Length)
for ($i = 0; $i -le $minindex; $i++) {
if ( $a1[$i] -ne $a2[$i] ) { $i }
}
for ( $i = $minindex + 1; $i -le $maxindex; $i++ ) { $i }
}
$array1 = #(1,2,3,4,5,6,7,23,44)
$array2 = #(1,1,3,4,5,7,6,23,45)
$array3 = Compare-Indexes $array1 $array2