Powershell processing arrays in a multi dimensional array after defined number - arrays

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.

Related

Get indexes of repeated values in array

I want to find indexes of repeated values in array.
E.g.
Input:
$data = #(1, 2, 3, 2, 1)
Output:
$indexes = #(3, 4)
For a different approach, you can use try-catch blocks with a hash table.
$data = #(1, 2, 3, 2, 1)
$hash = #{}
$indexes = for ($i = 0; $i -lt $data.count; $i++ ) {
try {
$hash.add($data[$i],$i)
}
catch {
$i
continue
}
}
# Output
$indexes
3
4
The idea here is to add each value as a key and the corresponding index as a value to the hash table. Since a [hashtable] object can only have unique keys, an exception will be thrown and caught. In the catch block, we just output the index which is ultimately stored in $indexes. The continue statement allows the loop to increment and keep processing.
Algorithmically speaking this solution is almost identical to the already proposed solution. However, it utilizes the more efficient Add() method of [arraylist] rather than rebuilding (+=) an [array] during each iteration. The performance is negligible in this example but could be worth considering in larger data sets. This also opts for the traditional for loop rather than foreach.
$uniqueValues = [collections.arraylist]#()
$indexes = for ($i = 0; $i -lt $data.count; $i++) {
if ($uniqueValues -contains $data[$i]) {
$i
}
else {
[void]$uniqueValues.Add($data[$i])
}
}
# Output
$indexes
3
4
This solution maintains an [arraylist] of unique values ($uniqueValues). Any value that is not unique, its index ($i) is output and stored in $indexes. Uniqueness is determined by using the -contains operator to compare the current value in the $data iteration to what is already in $uniqueValues.
You could also use a Hashtable for this:
$data = 1, 2, 3, 2, 1
$hash = #{}
$indexes = for ($i = 0; $i -lt $data.Count; $i++) {
# the value you store in the $hash in the else block is not important
if ($hash.ContainsKey($data[$i])) { $i } else {$hash[$data[$i]] = $true}
}
$indexes
Result:
3
4
Foreach loop with if inside should do the job:
$data = #(1, 2, 3, 2, 1)
$uniqueValues = #()
$duplicatesIndexes = #()
$data | ForEach-Object {$counter = 0}{
if ($_ -notin $uniqueValues) {
$uniqueValues += $_
} else {
$duplicatesIndexes += $counter
}
$counter++
}
# Output
PS> $duplicatesIndexes
3
4

Powershell combine the elements of two arrays into one another

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

How to combine every element in an array with every element in multiple other arrays in Powershell

In Powershell, I am trying to combine the elements of several arrays to create an array of unique strings. I need to combine every element from each array with every element in the other arrays. It is difficult to concisely explain what I mean, so it may be easier to show.
I start with a 2d-array that looks something like this:
$array = #('this', ('A','B','C'), ('1','2','3','4'), 'that')
I need to create an array, whose contents will look like this:
thisA1that
thisA2that
thisA3that
thisA4that
thisB1that
thisB2that
thisB3that
thisB4that
thisC1that
thisC2that
thisC3that
thisC4that
The length and number of arrays in the original array are variable, and I don't know the order of the items in the original array.
So far, I've tried a few methods, but my logic has been wrong. Here was my first attempt at a solution:
$tempList = #()
#get total number of resources that will be created
$total = 1
for($i=0; $i -lt $array.Count; $i++){$total = $total * $array[$i].Count}
# build each resource from permutations of array parts
for ($i = 0; $i -lt $array.Count; $i++)
{
for ($j = 0; $j -lt $total; $j++)
{
$tempList += #('')
$idx = $total % $array[$i].Count
# item may either be string or an array. If string, just add it. If array, add the item at the index
if ($array[$i] -is [array]){ $tempList[$j] += $array[$i][$idx] }
else { $tempList[$j] += $array[$i] }
}
}
In this example, my logic with the modulus operator was wrong, so it would grab the only the first index of each array every time. Upon further consideration, even if I fix the modulus logic, the overall logic would still be wrong. For example, if the second two arrays were the same size, I would get 'A' paired with '1' each time, 'B' with '2', etc.
I'm sure there is a way to do this, but I simply can't seem to see it.
I think the answer's to use recursion so you can handle the fact that the array of arrays can be any length. This recursive function should:
take the first array from the array-of-arrays
loop through each item in that first array
if the first array is also the last array, just return each item.
if there are more arrays in the array-of-arrays then pass the remaining arrays to the recursive function
for each result from the recursive function, prefix the return value with the current item from the first array.
I think the code explains itself better than the above:
function Combine-Array {
[CmdletBinding()]
Param (
[string[][]]$Array
)
Process {
$current = $Array[0]
foreach ($item in $current) {
if ($Array.Count -gt 1) {
Combine-Array ([string[][]]#($Array | Select -Skip 1)) | %{'{0}{1}' -f $item, $_}
} else {
$item
}
}
}
}
Combine-Array #('this', ('A','B','C'), ('1','2','3','4'), 'that')
You can write pipeline function, which would add one more subarray into the mix. And then you call it as many times as many subarrays you have:
function CartesianProduct {
param(
[array] $a
)
filter f {
$i = $_
$a[$MyInvocation.PipelinePosition - 1] | ForEach-Object { $i + $_ }
}
Invoke-Expression ('''''' + ' | f' * $a.Length)
}
CartesianProduct ('this', ('A','B','C'), ('1','2','3','4'), 'that')

Powershell foreach loop with additional variable and break

I am still new to powershell, right now I study foreach with break, I understand the concept, but when it combined with additional variable and break; it confuses me, here is the code:
$i=0
$varZ = (10,20,30,40)
foreach ($var in $varZ)
{
$i++
if ($var -eq 30)
{
break
}
}
Write-Host "30 was found in array position $i"
the result I get showing that variable $i = 3, where $var = 30
but what confuses me, as I understand $i starts with 0 and there is an array $varZ (10,20,30,40), as I understand when $i = 0 $var = 10, hence $i = 3 $var = 40? please correct me and help me understand this code
You are incrementing $i before you do your conditional check; whereas; it should be done after your break statement. Although $i is set to 0 before you begin your loop, you immediately increment by 1 with your statement $i++; thus, when $var is 10, $i which was 0 not become 0+1=1 and so forth.

Powershell: multidimensional array changes return value from "system.object[]"

I wondered if anyone could shed some light on the issue I am facing when returning values from a multidimensional array through a function:
$ArrayList = #()
function MultiDimensionalArrayTest
{
param(
[array]$ArrayList
)
for($i = 0; $i -lt 1; $i++)
{
$ArrayModify += ,#("Test", "Test")
}
return $ArrayModify
}
$ArrayModify = MultiDimensionalArrayTest
foreach ($item in $ArrayModify)
{
Write-Host $item[0]
}
When the loop is executed once the values returned are:
T
T
However if the for statement is lopped twice, the values returned are:
Test
Test
My aim is to retrieve x amount of values "Test" from the Write-Host $item[0] regardless of how many times the statement is executed
It appears that if two or more rows are captured and returned in the $ArrayModify array, the value is a system.object[], however if looped once, the value "Test, Test" is captured and when Write-Host $item[0] is executed, it will print T.
Any advice would be greatly appreciated.
Not the cleanest way of dealing with it but you need to prevent PowerShell from unrolling the single element array into an array with two elements.
function MultiDimensionalArrayTest{
$ArrayModify = #()
for($i = 0; $i -lt 1; $i++){
$ArrayModify += ,#("Test$i", "Test$i$i")
}
,#($ArrayModify)
}
Using the above function will get the desired output I believe. ,#($ArrayModify) ensures that the array is returned and not unrolled into its elements as you saw above.
$ArrayList = #()
$ArrayList = MultiDimensionalArrayTest
foreach ($item in $ArrayList){$item[0]}
Giving the output for $i -lt 1 in the loop
Test0
Giving the output for $i -lt 2 in the loop
Test0
Test1
Your Output
Concerning your output from your example with $i -lt 1 PowerShell is unrolling the array into a single dimension array with 2 elements "Test" and "Test". You are seeing the letter "T" since all strings support array indexing and will return the character from the requested position.
Other issues with code
Not to beat a dead horse but really look at the other answers and comments as they provide some tips as to some coding errors and anomalies of the code you presented in the question.
First of all I notice several mistakes in your code.
This line $ArrayList = #() is useless since you don't use the $ArrayList variable afterwards.
Regarding the MultiDimensionalArrayTest function, you declared an $ArrayList argument but you never use it in the function.
Finally when this line makes no sense $ArrayModify = MultiDimensionalArrayTest = $item, you probably meant $ArrayModify = MultiDimensionalArrayTest.
This is not a complete answer to your question, since I am not sure what you are trying to achieve, but take a look at this line:
$ArrayModify = MultiDimensionalArrayTest = $item
This makes no sense, since you are calling the function MultiDimensionalArrayTest and passing two arguments to it, which are "=" (powershell assumes this is a string) and $item (null object). Then you assign whatever is returned to $ArrayModify.
The reason "T" is outputted, is because you are outputting the first element of what is at the moment a string. "Test" is outputted when $item is an array of strings.

Resources