Powershell split an array into 5 arrays with equal length - arrays

Hello everyone hope you are all doing great! been searching but cannot get it right :( could it be possible for you to help me, please?
Need to split an array into 5 arrays with equal length, for example.
$MainArray = #(1,2,3,4,5,6,7,8,9,10,11)
Result:
array1 = 1,2,3
array2 = 4,5
array3 = 6,7
array4 = 8,9
array5 = 10,11
Each array as even as possible (order doesn't matters) has this and it splits but not as even as I would like to.
Currently, I have this (searched on the internet already)
function Split-Array {
[CmdletBinding()]
param(
[Object] $inArray,
[int]$parts
)
if ($inArray.Count -eq 1) { return $inArray }
$PartSize = [Math]::Ceiling($inArray.count / $parts)
$outArray = New-Object 'System.Collections.Generic.List[psobject]'
for ($i = 1; $i -le $parts; $i++) {
$start = (($i - 1) * $PartSize)
$end = (($i) * $PartSize) - 1
if ($end -ge $inArray.count) {$end = $inArray.count - 1}
$outArray.Add(#($inArray[$start..$end]))
}
return , $outArray
}
Split-array -inArray $MainArray -parts 5
This function splits the $MainArray into 5 arrays but not as even, the result is:
array1 = 1,2,3
array2 = 4,56
array3 = 7,8,9
array4 = 10,11
array5 = 11
It even errors adding 11 into 2 arrays. My brain is burned at this moment, haha any help would be much appreciated. thanks!

To perform the element distribution as requested - with extra elements getting added to the initial output arrays - use the following.
function Split-Array {
[CmdletBinding()]
param(
[object[]] $inArray,
[int] $parts
)
[int] $partSize = [Math]::Floor($inArray.count / $parts)
if ($partSize -eq 0) { throw "$parts sub-arrays requested, but the input array has only $($inArray.Count) elements." }
$extraSize = $inArray.Count - $partSize * $parts
$offset = 0
foreach ($i in 1..$parts) {
, $inArray[$offset..($offset + $partSize + [bool] $extraSize - 1)]
$offset += $partSize + [bool] $extraSize
if ($extraSize) { --$extraSize }
}
}
Note:
[bool] casts are used as a convenient shortcut to map nonzero values to 1 and zero to 0, via using the resulting [bool] in the context of calculations.
.. - the range operator - is used to extract array slices from the input array, and also as a simple way to loop $parts times via a foreach loop.
, - the array constructor operator - is used in its unary form to output each array slice as a whole - see this answer for an explanation.
Sample call, which uses ConvertTo-Json to visualize the results:
Split-array -inArray (1..11) -parts 5 |
ConvertTo-Json
Output (5 arrays with 2-3 elements each):
[
[
1,
2,
3
],
[
4,
5
],
[
6,
7
],
[
8,
9
],
[
10,
11
]
]

If you keep track of the arrays created, you should be able to get the results you're after:
Function Split-Array {
Param(
[object]$InputObject,
[int]$Chunks
)
$track = 1
while ($InputObject.Count -gt 0 -and $track -le $Chunks) {
$chunk_size = [Math]::Min($InputObject.Count, [Math]::Ceiling($InputObject.Count / ($Chunks - $track + 1)))
$chunk = $InputObject[0..($chunk_size - 1)]
$InputObject = $InputObject[$chunk_size..($InputObject.Count - 1)]
,$chunk
$track++
}
}
The while loop starts, and it will keep executing as long as either of these conditions are met:
$array.Count -gt 0: The count of elements in $array is greater than
0. This means that there are still elements in $array that need to be split into separate arrays.
$arrayIndex -le $arrayCount: The number of arrays created so far is
less than or equal to $arrayCount. This means that you haven't
created the desired number of arrays yet.

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

Errors when returning values to the array

I try to create a simple optimisation script. Here is my code:
# Analysis gives the initial inputs and outputs
$initialinputs
$initialoutputs
# The objective function
$F = ([math]::pow($initialinputs, 2)) * 2 - 3* $initialinputs
# Differentiation of the objective function
$DF = 2 * $initialinputs - 3
$ScaleFactor = 0.2
# If the optimum solution has been obtained, two termination measurements:
# maximum iteration and termination criterion(gradient)
$Maxloop = 100
$Termi = 0.001
# Create arrays
$InputsArr = #() #The array of inputs
$FunctionArr = #() #The array of function values
$DFunctionArr = #() # The array of differentiation values (Gradient)
# Calculations
#$InputsArr[0] = $initialinputs #The first input
#$FunctionArr[0] = $F[$InputsArr[0]]
#$DFunctionArr[0] = $DF[$inputsArr[0]]
for ($Innerloop = 1; $Innerloop -le $Maxloop; $Innerloop++)
{
# Calculate the second input
$InputsArr[$innerloop] = $InputsArr[$Innerloop - 1] - $ScaleFactor * (2 * $InputsArr[$Innerloop - 1] - 3)
$initialinputs = $InputsArr[$Innerloop]
# Calculate the function value
$FunctionArr[$innerloop] = ([math]::pow($initialinputs, 2)) * 2 - 3 * $initialinputs
Return, $FunctionArr
# Calculate the gradient value
$DFunctionArr[$innerloop] = 2 * $initialinputs - 3
return, $DFunctionArr
# If the gradient value less than the termination criterion (gradient),
# break the loop
if ($DFunctionArr[$innerloop] -le $Termi)
{
break
}
}
I created the empty arrays and then use the for loop to do the optimisation and store the outputs in the arrays. But I got some errors as shown below:
ERROR: + $InputsArr[$Innerloop] = $initialinputs
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException
ERROR: + FullyQualifiedErrorId : System.IndexOutOfRangeException
ERROR: + $FunctionArr[$innerloop] = $Functionoutput
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException
ERROR: + FullyQualifiedErrorId : System.IndexOutOfRangeException
I am not quite sure how to fix the errors. How to return the value to the arrays? Is += the only way to do so? I get confused about the arrays now.
When you initialize an array like this: $InputsArr = #(), powershell creates an array of length 0. Thus when you try and address this array with $InputsArr[$innerloop] it throws an error that element $innerloop does not exist.
Two solutions. You can either explicitly initialize an array of a particular type and length:
$InputsArr = New-Object double[] $MaxLoop
Or, you can use the += operator in your code to add new values to an array:
$InputsArr += $InputsArr[$Innerloop - 1] - $ScaleFactor * (2 * $InputsArr[$Innerloop - 1] - 3) #Calculate the second input
+= creates a new array of length n+1, copies the old array, and then adds the new value, so is very inefficient for large arrays. See here.
PowerShell arrays defined by #() are dynamically sized. You can't index-access things in them unless they actually contain elements. That's why you're getting "index out of bounds" exceptions when you use $array[$index] in your for loop. The array simply doesn't have a field $index yet.
Basically there are two ways of filling PowerShell arrays:
Append to an existing array in a loop:
$array = #()
for (...) {
$array += $foo
}
Echo the elements inside the loop and assign the loop output to a variable:
$array = for (...) {
$foo
}
The former should be avoided for larger numbers of array elements, as each append operation (+=) will create a new array with increased size, copy the elements, then assign the new array to the variable. The latter approach is far superior performance-wise. The only downside of the latter approach is that the result won't be an array unless the loop produces at least two elements. You can mitigate this by enforcing array output: #(for (...) {...}).
PowerShell also allows you to create arrays with fixed size:
$size = 100
$array = New-Object Object[] $size
However, there's usually no advantage to doing this.
Also note that the first instruction in your loop assumes that the array already contains an initial value:
$InputsArr[$innerloop] = $InputsArr[$Innerloop - 1] - $ScaleFactor * (2 * $InputsArr[$Innerloop - 1] - 3) #Calculate the second input
thus you need to prefill the array with one element before starting the loop:
$InputsArr = #($initialValue)
With that said, there are more PoSh ways of filling an array with values, e.g. like this:
$i = $initialValue
$array = #($i)
$array += 1..$Maxloop | ForEach-Object {
$i -= $ScaleFactor * (2 * $i - 3)
$i
}
If you want to fill multiple arrays in the same loop (because you can't use one loop per array for some reason) appending is probably your best option. Like this:
$InputsArr = #($initialinputs)
$FunctionArr = #($initialinputs)
$DFunctionArr = #($initialinputs)
for ($i = 1; $i -le $Maxloop -and $DFunctionsArr[-1] -le $Termi; $i++) {
$InputsArr += $InputsArr[-1] - $ScaleFactor * (2 * $InputsArr[-1] - 3)
$FunctionArr += ([Math]::Pow($InputsArr[-1], 2)) * 2 - 3 * $InputsArr[-1]
$DFunctionArr += 2 * $InputsArr[-1] - 3
}
or like this, if you don't need $InputsArr later on:
$FunctionArr = #($initialinputs)
$DFunctionArr = #($initialinputs)
$v = $initialinputs
for ($i = 1; $i -le $Maxloop -and $DFunctionsArr[-1] -le $Termi; $i++) {
$v -= $ScaleFactor * (2 * $v - 3)
$FunctionArr += [Math]::Pow($v, 2) * 2 - 3 * $v
$DFunctionArr += 2 * $v - 3
}

Is it possible to assign two variables in Perl foreach loop?

Is it possible to assign two variables the same data from an array in a Perl foreach loop?
I am using Perl 5, I think I came across something in Perl 6.
Something like this:
my $var1;
my $var2;
foreach $var1,$var2 (#array){...}
It's not in the Perl 5 core language, but List::Util has a pairs function which should be close enough (and a number of other pair... functions which may be more convenient, depending on what you're doing inside the loop):
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use List::Util 'pairs';
my #list = qw(a 1 b 2 c 3);
for my $pair (pairs #list) {
my ($first, $second) = #$pair;
say "$first => $second";
}
Output:
a => 1
b => 2
c => 3
The easiest way to use this is with a while loop that calls splice on the first two elements of the array each time,
while (my($var1, $var2) = splice(#array, 0, 2)) {
...
}
However, unlike foreach, this continually does a double-shift on the original array, so when you’re done, the array is empty. Also, the variables assigned are copies, not aliases as with foreach.
If you don’t like that, you can use a C-style for loop:
for (my $i = 0; $i < #array; $i += 2) {
my($var1, $var2) = #array[$i, $i+1];
...
}
That leaves the array in place but does not allow you to update it the way foreach does. To do that, you need to address the array directly.
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #pairlist; $i += 2) {
$pairlist[ $i + 0 ] x= 2;
$pairlist[ $i + 1 ] *= 2;
}
print "Array is #pairlist\n";
That prints out:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
You can get those into aliased variables if you try hard enough, but it’s probably not worth it:
my #kvlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #kvlist; $i += 2) {
our ($key, $value);
local(*key, $value) = \#kvlist[ $i, $i + 1 ];
$key x= 2;
$value *= 2;
}
print "Array is #kvlist\n";
Which prints out the expected changed array:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
Note that the pairs offered by the List::Pairwise module, which were but very recently added to the core List::Util module (and so you probably cannot use it), are still not giving you aliases:
use List::Util 1.29 qw(pairs);
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for my $pref (pairs(#pairlist)) {
$pref->[0] x= 2;
$pref->[1] *= 2;
}
print "Array is #pairlist\n";
That prints out only:
Array is fee 1 fie 2 foe 3 fum 4
So it didn’t change the array at all. Oops. :(
Of course, if this were a real hash, you could double the values trivially:
for my $value (values %hash) { $value *= 2 }
The reasons that works is because those are aliases into the actual hash values.
You cannot change the keys, since they’re immutable. However, you can make a new hash that’s an updated copy of the old one easily enough:
my %old_hash = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
my %new_hash;
#new_hash{ map { $_ x 2 } keys %old_hash } =
map { $_ * 2 } values %old_hash;
print "Old hash is: ", join(" " => %old_hash), "\n";
print "New hash is: ", join(" " => %new_hash), "\n";
That outputs
Old hash is: foe 3 fee 1 fum 4 fie 2
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2
A general algorithm for more than 2 variables:
while( #array ){
my $var1 = shift #array;
my $var2 = shift #array;
my $var3 = shift #array;
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PS: Using a working copy of the array to that it is preserved for use later:
if( my #working_copy = #array ){
while( #working_copy ){
my $var1 = shift #working_copy;
my $var2 = shift #working_copy;
my $var3 = shift #working_copy;
# other variables from #working_copy
# do things with $var1, $var2, $var3, ...
}
}
PPS: another way is to use indexing. Of course, that is a sure sign that the data structure is wrong. It should be an array of arrays (AoA) or an array of hashes (AoH). See perldoc perldsc and perldoc perllol.
my $i = 0;
while( $i < #array ){
my $var1 = $array[ $i++ ];
my $var2 = $array[ $i++ ];
my $var3 = $array[ $i++ ];
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PPPS: I've been asked to clarify why the data structure is wrong. It is a flatten set of tuples (aka records aka datasets). The tuples are recreated by counting of the number of data for each. But what is the reader constructing the set has a bug and doesn't always get the number right? If, for a missing value, it just skips adding anything? Then all the remaining tuples are shifted by one, causing the following tuples to be grouped incorrectly and therefore, invalid. That is why an AoA is better; only the tuple with the missing data would be invalid.
But an better structure would be an AoH. Each datum would access by a key. Then new or optional data can be added without breaking the code downstream.
While I'm at it, I'll add some code examples:
# example code for AoA
for my $tuple ( #aoa ){
my $var1 = $tuple->[0];
my $var2 = $tuple->[1];
my $var3 = $tuple->[2];
# etc
}
# example code for AoH
for my $tuple ( #aoh ){
my $var1 = $tuple->{keyname1};
my $var2 = $tuple->{key_name_2};
my $var3 = $tuple->{'key name with spaces'};
my $var4 = $tuple->{$key_name_in_scalar_variable};
# etc
}
Here is a module-less way to "loop" by an arbitrary value ($by) and output the resulting group of elements using an array slice:
#!perl -l
#array = "1".."6";
$by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
print "#array[$i..$i+$by]";
$i++ ;
}
As a one-liner to test (cut and paste to a Unix shell):
perl -E '#array = "1".."6"; $by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
say "#array[$i..$i+$by]"; $i++ }'
Output:
1 2 3
4 5 6
If you make $by = 2; it will print pairs of numbers. To get at specific elements of the resulting slice access it as an anonymous array: (e.g. [#array[$i..$i+$by]]->[1]).
See also:
How do I read two items at a time in a Perl foreach loop?
Perl way of iterating over 2 arrays in parallel
Some good responses there, including reference to natatime which is quite easy to use. It's easy to implement too - it is essentially a wrapper around the splice solutions mentioned in the responses here.
The following is not the nicest example, but I've been using autobox::Core and made an #array->natatime() "method" ;-) like this:
use autobox::Core ;
sub autobox::Core::ARRAY::natatime {
my ($self, $by) = #_;
my #copy = #$self ;
my #array ;
push #array, [splice (#copy, 0, $by) ] while #copy ;
if ( not defined wantarray ) {
print "#{ $_ } \n" for #array ;
}
return wantarray ? #array : \#array;
}
The #copy array is spliced destructively, but $self (which is how the #array in front of the autobox method -> arrow gets passed to the function) is still there. So I can do:
my #dozen = "1" .. "12" ; # cakes to eat
#dozen->natatime(4) ; # eat 4 at time
my $arr_ref = #dozen->natatime(4) ; # make a reference
say "Group 3: #{ $arr_ref->[2] }" ; # prints a group of elements
say scalar #dozen , " cakes left" ; # eat cake; still have it
Output:
1 2 3 4
5 6 7 8
9 10 11 12
Group 3: 9 10 11 12
12 cakes left
One other approach that also uses a CPAN module (I gave this answer elsewhere but it is worth repeating). This can also be done non-destructively, with Eric Strom's excellent List::Gen module:
perl -MList::Gen=":all" -E '#n = "1".."6"; say "#$_" for every 2 => #n'
1 2
3 4
5 6
Each group of elements you grab is returned in an anonymous array so the individual values are in: $_->[0] $_->[1] ... etc.
You mentioned Perl6, which handles multiple looping values nicely:
my #qarr = 1 .. 6;
my ($x, $y, $z) ;
for #qarr -> $x , $y , $z { say $x/$y ; say "z = " ~ $z }
Output:
0.5
z = 3
0.8
z = 6
For more on the Perl6 approach see: Looping for Fun and Profit from the 2009 Perl6 Advent Calendar, or the Blocks and Statements Synopsis for details. Perhaps Perl 5 will have a similar "loop by multliple values" construct one day - à la perl5i's foreach :-)

Manipulating arrays: Inserting new element to a certain index and shifting other elements

I have an array say
my #array = (1,4,5,8);
Each element of above array may or may not have a child.
Suppose 1 has 2,3 as children and 5 has 10 as a child.
I have to manipulate array such that it becomes 1,2,3,4,5,10,8
What I'm doing at current
foreach (#$children_indexes){ #myarray
foreach ($self->{RELATION}[$_]->{CHILDREN}){ #find the child of each index
push #$children_indexes, #$_; #I need to change this, as this is pushing at the end
}
}
Perhaps just use map instead:
use strict;
use warnings;
my #array = ( 1, 4, 5, 8 );
my %children = (
1 => [ 2, 3 ],
5 => [ 10 ],
);
my #new_array = map { ($_, #{ $children{$_} // [] }) } #array;
print "#new_array\n";
Outputs:
1 2 3 4 5 10 8
I'm guessing $self->{RELATION}[$_]->{CHILDREN} is an arrayref?
Either loop through your array of indexes by index and backwards:
for my $index_index ( reverse 0..$#$children_indexes ) {
if ( $self->{RELATION}[$children_indexes->[$index_index]]{CHILDREN} ) {
splice #$children_indexes, $index_index+1, 0, #{ $self->{RELATION}[$children_indexes->[$index_index]]{CHILDREN} };
}
}
or use map:
my #array_with_children = map { $_, #{ $self->{RELATION}[$_]{CHILDREN} || [] } } #$children_indexes;
(both assume ...->{CHILDREN} will be nonexist, or false at any rate, if no children)
Don't see why he should be using map this can be done perfectly fine with arrays.
With this you can get the index of the current element in your loop to see where you are adding:
my #array = qw(A B C E F G);
my $search = "C";
my %index;
#index{#array} = (0..$#array);
my $index = $index{$search}; < - getting the index of the curr element
print $index, "\n";
my #out_array;
my $insert = 'D';
push #out_array,
#array[0..$index],
$insert,
#array[$index+1..$#array];
print #array;
print "\n";
print #out_array;
Here is a working example of how this can be done :).

Loop over one dimension of a multi-dimensional array in Perl using for each

* UPDATED* for typos
Another PERL question.... I am trying to loop through a 2D array. I am positive about the size of one dimension but unsure on the second. The code snippet:
foreach my $value (#surfaces[1])
{
my $sum = 0;
my $smallest = 9999;
my $limit_surface = 0;
for (my $i = 0; $i < 3; $i++)
{
$sum += $surfaces[$i][$counter];
if ($surfaces[$i][$counter] <= $smallest)
{
$smallest = $surfaces[$i][$counter];
$limit_surface = $subchannel_number[$i];
}
}
$counter++;
push(#avg_value,$sum/#rodsurface_number);
push(#limiting_schan,$limit_surface);
push(#limiting_value,$smallest);
}
I am compiled but $value variable is failing to initialize.
Repeat after me:
Perl does not have multidimensional arrays
Perl does not have multidimensional arrays
Perl does not have multidimensional arrays
What Perl does have is have are arrays that contain references pointing to other arrays. You can emulate multidimensional arrays in Perl, but they are not true multidimensional arrays. For example:
my #array;
$array[0] = [ 1, 2, 3, 4, 5 ];
$array[1] = [ 1, 2, 3 ];
$array[2] = [ 1, 2 ];
I can talk about $array[0][1], and $array[2][1], but while $array[0][3] exists, $array[2][3] doesn't exist.
If you don't understand references, read the tutorial on references.
What you need to do is go through your array and then find out the size of each subarray and go through each of those. There's no guarantee that
The reference contained in your primary array actually points to another array:
That your sub-array contains only scalar data.
You can use the $# operator to find the size of your array. For example $#array is the number of items in your array. You an use ( 0..$#array ) to go through each item of your array, and this way, you have the index to play around with.
use strict;
use warnings;
my #array;
$array[0] = [ 1, 2, 3, 4, 5 ];
$array[1] = [ 1, 2, 3 ];
$array[2] = [ 1, 2, ];
#
# Here's my loop for the primary array.
#
for my $row ( 0..$#array ) {
printf "Row %3d: ", $row ;
#
# My assumption is that this is another array that contains nothing
# but scalar data...
#
my #columns = #{ $array[$row] }; # Dereferencing my array reference
for my $column ( #columns ) {
printf "%3d ", $column;
}
print "\n";
}
Note I did my #columns = #{ $array[$row] }; to convert my reference back into an array. This is an extra step. I could have simply done the dereferencing in my for loop and saved a step.
This prints out:
Row 0: 1 2 3 4 5
Row 1: 1 2 3
Row 2: 1 2
I could put some safety checks in here. For example, I might want to verify the size of each row, and if one row doesn't match the other, complain:
my $row_size = $array[0];
for my $row ( 1..$#array ) {
my #columns = #{ $array[$row] };
if ( $#columns ne $array_size ) {
die qq(This is not a 2D array. Not all rows are equal);
}
}
You do not describe your data structure, nor explain exactly what you want to do with it. This limits the advice that we can give to just the general variety.
If you're trying to iterate over an array of arrays, I would advise you to do it based off of element instead of index.
For example, below I have a 4 by 5 matrix of integers. I would like to find the average of these values. One way to do this is to simply iterate over each row and then column, and add up the values:
use strict;
use warnings;
my #AoA = (
[11, 12, 13, 14, 15],
[21, 22, 23, 24, 25],
[31, 32, 33, 34, 35],
[41, 42, 43, 44, 45],
);
my $sum = 0;
my $count = 0;
for my $row (#AoA) {
for my $element (#$row) { # <-- dereference the array ref
$sum += $element;
$count++;
}
}
print "Average of Matrix is " . ($sum / $count) . "\n";
Outputs:
Average of Matrix is 28
For more information on complex data structures, check out: Perl Data Structures Cookbook
I've set up some dummy variables and changed a few things around. This compiles and produces the results I show below.
This might not answer your question, but should allow you to copy and paste the code, run it yourself, edit the input and see how the output compares to what you want.
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
my #surfaces = ( ['1','2','3'],
['10','20','30'],
['100','200','400'],
);
my #subchannel_number = ( ['1','2','3'],
['10','20','30'],
['100','200','400'],
);
my #rodsurface_number = (1 .. 10);
my $counter = 0;
my (#avg_value, #limiting_schan, #limiting_value);
foreach my $value ($surfaces[1]){
my $sum = 0;
my $smallest = 9999;
my $limit_surface = 0;
for (my $i = 0; $i < 3; $i++) {
$sum += $surfaces[$i][$counter];
if ($surfaces[$i][$counter] <= $smallest){
$smallest = $surfaces[$i][$counter];
$limit_surface = $subchannel_number[$i];
}
}
$counter++;
push(#avg_value,$sum/#rodsurface_number);
push(#limiting_schan,$limit_surface);
push(#limiting_value,$smallest);
}
print Dumper (\#avg_value, \#limiting_schan, \#limiting_value);
$VAR1 = [
'11.1'
];
$VAR2 = [
[
'1',
'2',
'3'
]
];
$VAR3 = [
1
];

Resources