How to construct an array of arrays with Powershell? [duplicate] - arrays

This question already has answers here:
Powershell Multidimensional Arrays
(11 answers)
Closed 3 years ago.
To group data, every time there's a "new person" as below, I want to add their info to that temporary array and reset that array to null.
Before each "new person" array is set to null, I want to add that array to an array of people. An array of arrays.
How can I add one array into another?
$people = import-csv "./people.csv"
$h = #{}
$h.gettype()
$all_people
ForEach ($person in $people) {
$new_person
if ($person -match '[0-9]') {
Write-host $person
}
else {
write-host "new person"
write-host $person
}
}
output:
thufir#dur:~/flwor/people$
thufir#dur:~/flwor/people$ pwsh foo.ps1
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
new person
#{people=joe}
#{people=phone1}
#{people=phone2}
#{people=phone3}
new person
#{people=sue}
#{people=cell4}
#{people=home5}
new person
#{people=alice}
#{people=atrib6}
#{people=x7}
#{people=y9}
#{people=z10}
thufir#dur:~/flwor/people$
I have something like this:
$people = import-csv "./people.csv"
$all_people
$new_person = "new","person"
$new_person.GetType()
ForEach ($person in $people) {
if ($person -match '[0-9]') {
Write-host $person
$new_person.Add($person)
}
else {
write-host "new person"
write-host $person
#$new_person = null
$new_person = "new","person"
}
}

Powershell doesnt provide a good functionality to create an array of array with use of basic arrays.
What you can do use use an array of hashtable or PsCustomObjects to create yourself an array of arrays.

Yes you can create array of arrays.
Example we create 3 arrays like
$a = 1..5
$b = 6..10
$c = 11..15
Now we can add them in another array $d
$d = $a,$b,$c
Now we can access them like:
$d[0]
# Output
# 1
# 2
# 3
# 4
# 5
$d[0][2]
# Output is $a arrays 3rd element

Related

Paired enumeration of two arrays [duplicate]

This question already has answers here:
Powershell foreach regarding multiple collections
(1 answer)
Display multiple array values in list in Powershell
(2 answers)
Closed 3 months ago.
I have 2 arrays: I need to sort them in foreach to modify 1 file.
1 array with dbs name like:
$dbs = ['Honda', 'Toyota', 'BMW', 'Opel']
2nd array with their size like:
$dbsSize = ['123', '300', '222', '143']
and I can sort names in foreach like
foreach ($db in $dbs){
# some logic..
}
How can I sort 2nd array in the same foreach so I can use it in the same loop
Like this:
foreach ($db in $dbs) {
# some logic with $db
# and here I need some logic with $dbsSize
}
but to sort out $dbsSize like $dbs because I need the result like :
Honda
123
Toyota
300
etc.
Do I need to create another loop?
It seems to me you have two arrays where the indices determine which element of the one array belongs to the element in the other array.
It is unclear how you created them and why you did not create an array of objects with both properties together in the first place, but you can still do that like this:
$dbs = 'Honda', 'Toyota', 'BMW', 'Opel'
$dbsSize = '123', '300', '222', '143'
$result = for($i = 0; $i -lt [math]::Max($dbs.Count, $dbsSize.Count); $i++) {
[PsCustomObject]#{
Car = $dbs[$i]
Size = $dbsSize[$i]
}
}
# sort the items on 'Car' for instance
$result = $result | Sort-Object Car
# show on screen
$result
# save to structured CSV file you can open in Excel
$result | Export-Csv -Path 'X:\Somewhere\cars.csv' -UseCulture -NoTypeInformation
Result on screen using the above exampe looks like
Car Size
--- ----
BMW 222
Honda 123
Opel 143
Toyota 300
I don't see any sorting in your code, I only see enumeration. Seems like you want to enumerate both collections sequentially, in that case you would need to use a for loop instead of foreach:
$dbs = "['Honda', 'Toyota', 'BMW', 'Opel']" | ConvertFrom-Json
$dbsSize = "['123', '300', '222', '143']" | ConvertFrom-Json
for($i = 0; $i -lt [math]::Max($dbs.Count, $dbsSize.Count); $i++) {
$dbs[$i]
$dbsSize[$i]
}
If you want to use a foreach loop and both collections have the same Length, then you can get the items of one of them (your choice which) via indexing [ ]:
$i = 0
foreach($item in $dbs) {
$item
$dbsSize[$i++]
}
If you're looking to merge both arrays into objects, an easy way to do it is with the function from this answer, the usage in this case would be:
$dbs, $dbsSize | Join-Array -Columns Car, Size

How to declare a multidimensional 2D array in Powershell? [duplicate]

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

PowerShell array, loop first row

I have a problem with array as following with only one line:
$list = #()
$list = (("ResourceGroup","Vm1"))
$list | ForEach-Object -Parallel {
write-output $_[0] $_[1]
}
If I loop that array with one line, PowerShell prints the first 2 letter of each word. If I put 2 or more row like following:
$list = #()
$list = (("ResourceGroup","Vm1"),`
("ResourceGroup","Vm2")
)
PowerShell print correctly the values inside.
There is a way to print correctly the value of an array with only one line?
("ResourceGroup","Vm1") is interpreted as one array with two string elements, where as (("ResourceGroup","Vm1"), ("ResourceGroup","Vm2")) is interpreted as one array with 2 array elements, can be also called a jagged array. If you want to ensure that the first example is treated the same as the second example, you can use the comma operator ,:
$list = , ("ResourceGroup","Vm1")
$list | ForEach-Object -Parallel {
Write-Output $_[0] $_[1]
}
To put it in perspective:
$list = ("ResourceGroup","Vm1")
$list[0].GetType() # => String
$list = , ("ResourceGroup","Vm1")
$list[0].GetType() # => Object[]
Write-Output with the -NoEnumerate switch combined with the Array subexpression operator #( ) can be another, more verbose, alternative:
$list = #(Write-Output "ResourceGroup", "Vm1" -NoEnumerate)
$list | ForEach-Object -Parallel {
Write-Output $_[0] $_[1]
}

Compare duplicates in an array. List values that are contained and not contained in array [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have these arrays, I want to list unique values. If a value in $arr2 is in $arr1 I want to list that out but only ONE time. If a value in $arr2 is NOT in $arr1 I want to list that out as well. I would like to also add the values in $arr2 that are not contained in $arr1 to a seperate array.
This is written in powershell but if you have a solution/tips in any other language that is perfectly fine with me I will rewrite to powershell.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2= #(1,2,3,4,5,6,7)
for ($i = 0; $i -lt $arr2.length; $i++){
for( $j = $i+1; $j -lt $arr1.length; $j++ ){
if($arr2[$i] -eq $arr1[$j]){
Write-Host $arr2[$i]
}
}
}
You can use something like this to create an object which can be used for your need, however it will require further code from your side (i.e.: filtering) to get the results you're looking for.
$arr1 = #(1,1,1,2,2,3,4,4,5,7)
$arr2 = #(1,2,3,4,5,6,7)
$result = [System.Collections.Generic.List[pscustomobject]]::new()
foreach($i in $arr1 + $arr2)
{
if($i -in $result.Value)
{
continue
}
$z = [ordered]#{
Value = $i
Array1 = $false
Array2 = $false
}
if($i -in $arr1)
{
$z.Array1 = $true
}
if($i -in $arr2)
{
$z.Array2 = $true
}
$result.Add([pscustomobject]$z)
}
$valuesOnBothArrays, $valuesOnOneArray = $result.Where({
$_.Array1 -eq $true -and $_.Array2 -eq $true}, 'Split'
)
$valuesOnBothArrays will result in:
Value Array1 Array2
----- ------ ------
1 True True
2 True True
3 True True
4 True True
5 True True
7 True True
$valuesOnOneArray will result in:
Value Array1 Array2
----- ------ ------
6 False True
I suggest using the Compare-Object cmdlet in combination with the .Where() array method:
$arr1 = 1,1,1,2,2,3,4,4,5,7
$arr2 = 1,2,3,4,5,6,7
$inBoth, $uniqueRight =
(Compare-Object -PassThru -IncludeEqual `
($arr1 | Select-Object -Unique) ($arr2 | Select-Object -Unique)).
Where({ $_.SideIndicator -in '==', '=>' }).
Where({ $_.SideIndicator -eq '==' }, 'Split')
"-- in both:"
$inBoth
"-- unique to arr2"
$uniqueRight
Note: Thanks to using Select-Object -Unique on the input arrays so as to only operate on distinct elements, the use of -PassThru with Compare-Object works as expected and passes the integers of interest through directly, rather than as properties of wrapper objects. The caveat is that with [int] array elements specifically, because PowerShell caches values up to 100, having duplicates in either collection would malfunction obscurely. The reason is that -PassThru decorates the pass-through elements with an ETS .SideIndicator property, which affects all uses of a given integer between 0 and 100, so that the .SideIndicator property value of a later duplicate would overwrite the original value - see this answer for more information.
Note:
If you know that the distinct elements of $arr1 are only ever a subset of the ones in $arr2, i.e. that $arr1 contains no unique elements, you can eliminate the intermediate .Where({ $_.SideIndicator -in '==', '=>' }) call.
Unfortunately, as of PowerShell 7.2, the implementation of Select-Object -Unique is quite inefficient - see GitHub issue #11221. If the input arrays were sorted, Get-Unique would be a faster alternative.
The above yields:
-- in both (distinct):
1
2
3
4
5
7
-- unique to arr2
6

Convert a onedimensional Array to a multidimensional Array

I have an array in the following format:
C123456,
John Example,
C654321,
Mike Lastname,
C999999,
Elisabeth Average
Is there an easy way which I can convert that array to something like this:
CPName Name
C123456 John Example
C654321 Mike Lastname
C999999 Elisabeth Average
Iterate over the array with step size 2 and build custom objects from all even indexes and their next neighbor:
$list = for ($i=0; $i -lt $arr.Count; $i+=2) {
New-Object -Type PSObject -Property #{
'CPName' = $arr[$i].TrimEnd(',')
'Name' = $arr[$i+1].TrimEnd(',')
}
}

Resources