Powershell combine the elements of two arrays into one another - arrays

I'd like to join two arrays by picking the elements from each array one by one. and not have them combined or simply merged
I know how to add a second array to the first one as in:
$array1 = (0,4,8)
$array2 = (1,5,2)
$array1 += $array2
$array1
Which results in the following:
0
4
8
1
5
2
But how can I copy them into one another giving me an output like this:
0
1
4
5
8
2
Note: I don't want to merge them and then sort the list.
The elements need to stay in the same order. How would that be achieved?

Although Esperento57 gives you a perfect working solution, here's my idea that will also allow for arrays that are not of the same length. It uses a System.Collections.ArrayList to add the values from the arrays for better performance if you have large arrays to combine.
$array1 = (0,2,4)
$array2 = (1,3,5,6,7,8)
$len1 = $array1.Length
$len2 = $array2.Length
$maxLength = [Math]::Max($len1, $len2)
$listResult = New-Object System.Collections.ArrayList
for ($i = 0; $i -lt $maxLength; $i++) {
if ($i -lt $len1) { [void] $listResult.Add($array1[$i]) }
if ($i -lt $len2) { [void] $listResult.Add($array2[$i]) }
}
$listResult.ToArray()

try something like this
$array1 = (0,2,4)
$array2 = (1,3,5)
$MaxLen=[Math]::Max($array1.Length, $array2.Length)
$Result=#()
for ($i = 0; $i -lt $MaxLen; $i++)
{
$Result+=$array1[$i]
$Result+=$array2[$i]
}
$Result

here's another way to do it. [grin]
this one takes into account dissimilar sizes in the arrays and interleaves them until one array runs out of items. the remaining items in the larger array are then added without "ghost" items from the the smaller array.
$array1 = #(0,2,4)
$array2 = #(5,7,9,11)
$InterleavedArray = [System.Collections.Generic.List[int]]::new()
$UpperBound = [math]::Max($array1.GetUpperBound(0), $array2.GetUpperBound(0))
foreach ($Index in 0..$UpperBound)
{
if ($Index -le $array1.GetUpperBound(0))
{
$InterleavedArray.Add($array1[$Index])
}
if ($Index -le $array2.GetUpperBound(0))
{
$InterleavedArray.Add($array2[$Index])
}
}
$InterleavedArray
output ...
0
5
2
7
4
9
11
hope that helps,
lee

If you want the elements to stay in the same order, just do $array3 = $array1 + $array2. If you want to sort it though, do $array3 = ($array1 + $array2) | sort.

Here is a slightly modified version of Theos answer.
Looks cleaner and its faster:
$array1 = (0,2,4)
$array2 = (1,3,5,6,7,8)
$len1 = $array1.Length
$len2 = $array2.Length
$maxIndex = [Math]::Max($len1, $len2)-1
$arrayResult = #()
$arrayResult = foreach ($i in 0..$maxIndex) {
if ($i -lt $len1) { $array1[$i] }
if ($i -lt $len2) { $array2[$i] }
}
$arrayResult

Related

Powershell use values of one array as headings for another array

I have two arrays in Powershell:
$headings = ['h1', 'h2']
$values = [3, 4]
It is guaranteed that both arrays have the same length. How can I create an array where the values of $headings become the headings of the $values array?
I want to be able to do something like this:
$result['h2'] #should return 4
Update:
The arrays $headings and $values are of type System.Array.
As stated above you'll need a PowerShell hashtable. By the way array in PowerShell are defined via #(), see about_arrays for further information.
$headings = #('h1', 'h2')
$values = #(3, 4)
$combined = #{ }
if ($headings.Count -eq $values.Count) {
for ($i = 0; $i -lt $headings.Count; $i++) {
$combined[$headings[$i]] = $values[$i]
}
}
else {
Write-Error "Lengths of arrays are not the same"
}
$combined
Dumping the content of combined returns:
$combined
Name Value
---- -----
h2 4
h1 3
Try something like this :
$hash = [ordered]#{ h1 = 3; h2 = 4}
$hash["h1"] # -- Will give you 3
## Next Approach
$headings = #('h1', 'h2') #array
$values = #(3, 4) #array
If($headings.Length -match $values.Length)
{
For($i=0;$i -lt $headings.Length; $i++)
{
#Formulate your Hashtable here like the above and then you would be able to pick it up
#something like this in hashtable variable $headings[$i] = $values[$i]
}
}
PS: I am just giving you the logical headstart and helping you with the hashtable part. Code is upto you.

Powershell processing arrays in a multi dimensional array after defined number

if i need to process all arrays in a multi dimensional array after skipping the first one how would i go about this?
in this case adding + 5 to each value. what if i want to start at the second array $mdarr[1]<
cls
$mdarr = #()
$i = #()
$ii = #()
$mdarr = #((0,1,2,3,4),(5,6,7,8,9),(10,11,12,13,14))
for ($i = 0; $i -lt $mdarr.Length; ++$i){
for ($ii = 0; $ii -lt $mdarr[$i].Length; ++$i){
$mdarr = $mdarr[$i][$ii] + 5
}
}
write-host $mdarr
there is so much wrong with the above. the result i'm looking for should be:
((0,1,2,3,4),(10,11,12,13,14),(15,16,17,18,19))
how would this be done?
The problem is in updating the array contents. All that's needed is a nested loop to process elements in the inner arrays with appropriate indexing. Like so,
$mdarr = #((0,1,2,3,4),(5,6,7,8,9),(10,11,12,13,14))
for($i = 1; $i -lt $mdarr.Length; ++$i) {
for($j = 0; $j -lt $mdarr[$i].Length; ++$j) {
$mdarr[$i][$j] += 5
}
}
$mdarr[1]
10
11
12
13
14
As why didn't the original work, let's analyze the code and see what was wrong:
# This starts from 1st element (index 0), which was to be skipped. Bug
for ($i = 0; $i -lt $mdarr.Length; ++$i){
# Loop counters $ii and $i are confusing, name is almost same
# What's more, $i is increased instead of $ii. Bug
for ($ii = 0; $ii -lt $mdarr[$i].Length; ++$i){
# This doesn't make sense. It's overwriting the whole
# source array-of-arrays with a single value.
# The array cell was to be updated instead. Bug
$mdarr = $mdarr[$i][$ii] + 5
}
}
To sum up, the idea was there. Due indexing bugs and inappropriate assignment operation, the outcome was wrong. Still, fixing is quite straightforward, as the main logic was okay.

Powershell how to process a variable array

I have the following situation in powershell:
$arrayX = 0..1
$arrayY = 0..10
$array1 = #()
$array2 = #()
for ($i = 0; $i -lt $arrayY.Length; $i++){
$array1 += $arrayX[0] + $arrayY[$i]
$array2 += $arrayX[1] + $arrayY[$i]
}
Both $arrayX and $arrayY can be variable in length. If i extend $arrayX by 1 i'll need to adjust the code to take the third value into account. like this:
$arrayX = 0..2
$arrayY = 0..10
$array1 = #()
$array2 = #()
$array3 = #()
for ($i = 0; $i -lt $arrayY.Length; $i++){
$array1 += $arrayX[0] + $arrayY[$i]
$array2 += $arrayX[1] + $arrayY[$i]
$array3 += $arrayX[2] + $arrayY[$i]
}
What is the best practice in a situation like this to have this work automatic?
First, please consider not using the += operation with arrays: it will hurt performance a lot on larger arrays. Since you know the array size in advance you can allocate all required memory in advance:
$array1 = New-Object object[] $arrayY.Length
(you may want to use more specific type instead of object: int or float/double will work)
Next, instead of assigning each array to a variable, you can instead create array of arrays:
$arrayX = 0..2
$arrayY = 0..10
$resultArrays = New-Object int[][] $arrayX.Length
for ($x = 0; $x -lt $resultArrays.Length; ++$x)
{
$resultArrays[$x] = New-Object int[] $arrayY.Length
}
for ($y = 0; $y -lt $arrayY.Length; ++$y)
{
for ($x = 0; $x -lt $arrayX.Length; ++$x)
{
$resultArrays[$x][$y] = $arrayX[$x] + $arrayY[$y];
}
}
for ($x = 0; $x -lt $resultArrays.Length; ++$x)
{
Write-Output "array $($x): $($resultArrays[$x] -join ' ')"
}
Is this what you are looking for?
$arrayX = 0..2
$arrayY = 0..10
$arrayX | ForEach-Object {
$aX = $_
New-Variable -Name ('array' + $($aX+1)) -Value ($arrayY | ForEach-Object {$_ + $aX}) -Force
}

How to fill an array efficiently in Powershell

I want to fill up a dynamic array with the same integer value as fast as possible using Powershell.
The Measure-Command shows that it takes 7 seconds on my system to fill it up.
My current code (snipped) looks like:
$myArray = #()
$length = 16385
for ($i=1;$i -le $length; $i++) {$myArray += 2}
(Full code can be seen on gist.github.com or on superuser)
Consider that $length can change. But for better understanding I chose a fixed length.
Q: How do I speed up this Powershell code?
You can repeat arrays, just as you can do with strings:
$myArray = ,2 * $length
This means »Take the array with the single element 2 and repeat it $length times, yielding a new array.«.
Note that you cannot really use this to create multidimensional arrays because the following:
$some2darray = ,(,2 * 1000) * 1000
will just create 1000 references to the inner array, making them useless for manipulation. In that case you can use a hybrid strategy. I have used
$some2darray = 1..1000 | ForEach-Object { ,(,2 * 1000) }
in the past, but below performance measurements suggest that
$some2darray = foreach ($i in 1..1000) { ,(,2 * 1000) }
would be a much faster way.
Some performance measurements:
Command Average Time (ms)
------- -----------------
$a = ,2 * $length 0,135902 # my own
[int[]]$a = [System.Linq.Enumerable]::Repeat(2, $length) 7,15362 # JPBlanc
$a = foreach ($i in 1..$length) { 2 } 14,54417
[int[]]$a = -split "2 " * $length 24,867394
$a = for ($i = 0; $i -lt $length; $i++) { 2 } 45,771122 # Ansgar
$a = 1..$length | %{ 2 } 431,70304 # JPBlanc
$a = #(); for ($i = 0; $i -lt $length; $i++) { $a += 2 } 10425,79214 # original code
Taken by running each variant 50 times through Measure-Command, each with the same value for $length, and averaging the results.
Position 3 and 4 are a bit of a surprise, actually. Apparently it's much better to foreach over a range instead of using a normal for loop.
Code to generate above chart:
$length = 16384
$tests = '$a = ,2 * $length',
'[int[]]$a = [System.Linq.Enumerable]::Repeat(2, $length)',
'$a = for ($i = 0; $i -lt $length; $i++) { 2 }',
'$a = foreach ($i in 1..$length) { 2 }',
'$a = 1..$length | %{ 2 }',
'$a = #(); for ($i = 0; $i -lt $length; $i++) { $a += 2 }',
'[int[]]$a = -split "2 " * $length'
$tests | ForEach-Object {
$cmd = $_
$timings = 1..50 | ForEach-Object {
Remove-Variable i,a -ErrorAction Ignore
[GC]::Collect()
Measure-Command { Invoke-Expression $cmd }
}
[pscustomobject]#{
Command = $cmd
'Average Time (ms)' = ($timings | Measure-Object -Average TotalMilliseconds).Average
}
} | Sort-Object Ave* | Format-Table -AutoSize -Wrap
Avoid appending to an array in a loop. It's copying the existing array to a new array with each iteration. Do this instead:
$MyArray = for ($i=1; $i -le $length; $i++) { 2 }
Using PowerShell 3.0 you can use (need .NET Framework 3.5 or upper):
[int[]]$MyArray = ([System.Linq.Enumerable]::Repeat(2, 65000))
Using PowerShell 2.0
$AnArray = 1..65000 | % {2}
It is not clear what you are trying. I tried looking at your code. But, $myArray +=2 means you are just adding 2 as the element. For example, here is the output from my test code:
$myArray = #()
$length = 4
for ($i=1;$i -le $length; $i++) {
Write-Host $myArray
$myArray += 2
}
2
2 2
2 2 2
Why do you need to add 2 as the array element so many times?
If all you want is just fill the same value, try this:
$myArray = 1..$length | % { 2 }
If you need it really fast, then go with ArrayLists and Tuples:
$myArray = New-Object 'Collections.ArrayList'
$myArray = foreach($i in 1..$length) {
[tuple]::create(2)
}
and if you need to sort it later then use this (normally a bit slower):
$myArray = New-Object 'Collections.ArrayList'
foreach($i in 1..$length) {
$myArray.add(
[tuple]::create(2)
)
}
both versions are in the 20ms range for me ;-)

How can I compare two arrays, removing similar items, without iterating through the whole array?

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

Resources