Perl - Split Array in to Smaller Evenly Distributed Arrays - arrays

How would I split a Perl array of arbitrary size in to a variable number of smaller arrays with the number of elements in each smaller array being distibuted as evenly possible? The original array must not be destroyed.

Off the top of my head:
use strict;
use warnings;
use Data::Dumper; # for debugging only
print Dumper(distribute(7, [1..30]));
# takes number+arrayref, returns ref to array of arrays
sub distribute {
my ($n, $array) = #_;
my #parts;
my $i = 0;
foreach my $elem (#$array) {
push #{ $parts[$i++ % $n] }, $elem;
};
return \#parts;
};
This guarantees that number of elements in #parts may only differ by one. There's anonther solution that would count the numbers beforehand and use splicing:
push #parts, [ #$array[$offset..$offset+$chunk] ];
$offset += chunk;
# alter $chunk if needed.

Here's a version using List::MoreUtils:
use strict;
use warnings;
use List::MoreUtils qw(part);
use Data::Dumper;
my #array = 1..9;
my $partitions = 3;
my $i = 0;
print Dumper part {$partitions * $i++ / #array} #array;

If you don't care what winds up in each array:
use strict;
use warnings;
use List::MoreUtils qw(part);
use Data::Dumper;
my $i = 0;
my $numParts = 2;
my #part = part { $i++ % $numParts } 1 .. 30;
print Dumper #part;

#Dallaylaen's answer doesn't quite work because you can't pass an array into a subroutine in Perl. Instead of passing in an array (or a list as Dallaylaen did in his example) you must pass in a reference to an array:
my #arrayIn = (1..30);
my #arrayOfArrays = distribute(7, \#arrayIn);
sub distribute {
my ($n, $array) = #_;
my #parts;
my $i = 0;
foreach my $elem (#$array) {
push #{ $parts[$i++ % $n] }, $elem;
};
return #parts;
};

Related

For Loop Issues in creating nested array

Creating a matrix of products for three element arrays. I understand Perl does not have multi-dimensional arrays and are flattened. I have been using refs but I can't seem to get past the for loop issue in getting three products into a single array and pushing that array into a different single array. And I could be way off too. Be nice, but I've spent too many hours on this.
I have moved values inside and out of various places i.e. { }, printed out variables until I'm blue and used $last all over for debugging. I'm likely fried at this point.
use strict;
use warnings;
my #array1 = (1, 2, 3);
my #array2 = (2, 4, 6);
my #matrixArray = ();
my $matrixArray;
my #row;
my #finalArray = maths(\#array1, \#array2);
print #finalArray;
sub maths{
my $array1ref = shift;
my $array2ref = shift;
my $value1;
my $value2;
my $maths;
my #row = ();
my #array1 = #{$array1ref};
my #array2 = #{$array2ref};
my $len1 = #array1;
my $len2 = #array2;
for my $x (0 ..($len1 -1)){
#iterate through first array at each value
$value1 = $array1[$x];
#print $value1, " value1 \n";
for my $y (0 .. ($len2 -1)){
#iterate through second array at each value
$value2 = $array2[$y];
#print $value2, " value2 \n";
#calculate new values
$maths = $value1 * $value2;
#exactly right here
#print $maths, " maths \n" ;
push #row, $maths;
}
}
#and exactly right here but not set of arrays
#print #row, "\n";
return #row;
}
Currently I'm able to get this: 246481261218. Which is the correct dumb math but...
it should appear as a matrix:
2 4 6
4 8 12
6 12 18
I am not passing three arrays so it seems my issue is up in the sub routine before I can get on with anything else. This seems to be a theme that I often miss. So sorry if I sound inept.
EDIT***
This was working but I couldn't unpack it
use strict;
use warnings;
my #array1 = (1, 2, 3);
my #array2 = (2, 4, 6);
my #matrixArray = ();
maths(\#array1, \#array2);
foreach my $x (#matrixArray){
print "$x \n";
}
sub maths{
my $array1ref = shift;
my $array2ref = shift;
my $value1;
my $value2;
my $maths;
my #row = ();
my $row;
my #array1 = #{$array1ref};
my #array2 = #{$array2ref};
my $len1 = #array1;
my $len2 = #array2;
for my $x (0 ..($len1 -1)){
#iterate through first array at each value
$value1 = $array1[$x];
for my $y (0 .. ($len2 -1)){
#iterate through second array at each value
$value2 = $array2[$y];
#calculate new values
$maths = $value1 * $value2;
push #row, $maths;
$row = \#row;
}
push #matrixArray, $row;
}
return #matrixArray;
}
The output right after the function call is this:
ARRAY(0x55bbe2c667b0)
ARRAY(0x55bbe2c667b0)
ARRAY(0x55bbe2c667b0)
which would be the (line 10) print of $x.
****EDIT
This Works (almost):
print join(" ", #{$_}), "\n" for #matrixArray;
Output is a bit wrong...
2 4 6 4 8 12 6 12 18
2 4 6 4 8 12 6 12 18
2 4 6 4 8 12 6 12 18
And of note: I knew $x was an array but I seemed to run into trouble trying to unpack it correctly. And I'm no longer a fan of Perl. I'm pining for the fjords of Python.
And *****EDIT
This is working great and I get three arrays out of it:
sub maths{
my ($array1, $array2) = #_;
my #res;
for my $x (#$array1) {
my #row;
for my $y (#$array2) {
push #row, $x * $y;
}
push #res, \#row;
}
#This is the correct structure on print #res!
return #res;
}
But, though it's putting it together correctly, I have no output after the call
maths(\#array1, \#array2);
NOTHING HERE...
print #res;
print join(" ", #{$_}), "\n" for #res;
foreach my $x (#res){
print join(" ", #{$x}), "\n";
}
And of course a million thanks! I regret taking this stupid course and fear my grade will eventually do me in. Still pining for Python!
It appears that you need a matrix with rows obtained by multiplying an array by elements of another.
One way
use warnings;
use strict;
use Data::Dump qw(dd);
my #ary = (2, 4, 6);
my #factors = (1, 2, 3);
my #matrix = map {
my $factor = $_;
[ map { $_ * $factor } #ary ]
} #factors;
dd #matrix;
The array #matrix, formed by the outer map, has array references for each element and is thus (at least) a two-dimensional structure (a "matrix"). Those arrayrefs are built with [ ], which creates an anonymous array out of a list inside. That list is generated by map over the #ary.
I use Data::Dump to nicely print complex data. In the core there is Data::Dumper.
With a lot of work like this, and with large data, efficiency may matter. The common wisdom would have it that direct iteration should be a bit faster than map, but here is a benchmark. This also serves to show more basic ways as well.
use warnings;
use strict;
use feature 'say';
use Benchmark qw(cmpthese);
my $runfor = shift // 5; # run each case for these many seconds
sub outer_map {
my ($ary, $fact) = #_;
my #matrix = map {
my $factor = $_;
[ map { $_ * $factor } #$ary ]
} #$fact;
return \#matrix;
}
sub outer {
my ($ary, $fact) = #_;
my #matrix;
foreach my $factor (#$fact) {
push #matrix, [];
foreach my $elem (#$ary) {
push #{$matrix[-1]}, $elem * $factor;
}
}
return \#matrix;
}
sub outer_tmp {
my ($ary, $fact) = #_;
my #matrix;
foreach my $factor (#$fact) {
my #tmp;
foreach my $elem (#$ary) {
push #tmp, $elem * $factor;
}
push #matrix, \#tmp;
}
return \#matrix;
}
my #a1 = map { 2*$_ } 1..1_000; # worth comparing only for large data
my #f1 = 1..1_000;
cmpthese( -$runfor, {
direct => sub { my $r1 = outer(\#a1, \#f1) },
w_tmp => sub { my $r2 = outer_tmp(\#a1, \#f1) },
w_map => sub { my $r3 = outer_map(\#a1, \#f1) },
});
On a nice machine with v5.16 this prints
Rate direct w_map w_tmp
direct 11.0/s -- -3% -20%
w_map 11.4/s 3% -- -17%
w_tmp 13.8/s 25% 21% --
The results are rather similar on v5.29.2, and on an oldish laptop.
So map is a touch faster than building a matrix directly, and 15-20% slower than the method using a temporary array for rows, which I'd also consider clearest. The explicit loops can be improved a little by avoiding scopes and scalars, and the "direct" method can perhaps be sped up some by using indices. But these are dreaded micro-optimizations, and for fringe benefits at best.
Note that timings such as these make sense only with truly large amounts of data, what the above isn't. (I did test with both dimensions ten times as large, with very similar results.)
The second program is mostly correct.
The problem is that you didn't unpack the second level of the array.
foreach my $x (#matrixArray){
print "$x \n";
}
should be something like:
foreach my $x (#matrixArray) {
print join(" ", #{$x}), "\n";
}
# or just:
print join(" ", #{$_}), "\n" for #matrixArray;
Your maths function can be made shorter without losing legibility (it may actually make it more legible) by cutting out unnecessary temporary variables and indexing. For example:
sub maths {
my #array1 = #{ $_[0] };
my #array2 = #{ $_[1] }; # or: ... = #{ (shift) };
my #res = ();
for my $x (#array1) {
my #row = (); # <-- bugfix of original code
for my $y (#array2) {
my $maths = $x * $y;
push #row, $maths;
}
push #res, \#row;
}
return #res;
}

An elegant way of returning the index of the last non-zero element in Perl?

I find myself wanting to find the index of the last non-zero element in an array. So, given:
my #array = (0,0,5,9,0,0,0,7,0,3,0,0);
my $indexLastNonZero = insertElegantMethodHere(#array);
# expect $indexLastNonZero to be equal to 9;
I've done this:
for my $i (0 .. $#array) {
$indexLastNonZero = $i if $array[$i] != 0;
};
I works but somehow I can't help feel there must be a super elegant (smarter? nice? more efficient?) way of doing this in perl. I've looked into List::Utils but not found a nice way there and would like a non-core-module independent method.
Any thoughts?
Cheers
Use List::MoreUtils for such tasks:
use warnings;
use strict;
use List::MoreUtils;
my #array = (0,0,5,9,0,0,0,7,0,3,0,0);
print List::MoreUtils::lastidx { $_ } #array
Start at the end of the array and work backwards until you find a non-zero element:
my #array = (0,0,5,9,0,0,0,7,0,3,0,0);
my $i = $#array;
$i-- while $i >= 0 && $array[$i] == 0;
print "The last non-zero element is at index $i\n";
The $i >= 0 test is to guard against the edge case where all elements are zero. In that case the resulting value of $i is -1.
my #array = (0,0,5,9,0,0,0,7,0,3,0,0);
my ($indexLastNonZero) = grep $array[$_], reverse 0 .. $#array;
You could use List::Util, which is in core:
use strict;
use warnings;
use List::Util qw(first);
my #array = (0,0,5,9,0,0,0,7,0,3,0,0);
my $index = #array;
first { $index-- && $_ } reverse #array;
print "Last index that is non-zero: $index\n";
Destructive approach so take a copy of the array first:
my #array2 = #array;
while (!pop #array2) {} # Remove up to and including the last non-zero
print scalar #array2; # Size of remaining elements is index of last non-zero
sub last_true {
pop and return scalar #_ while #_;
undef;
}
my $index = last_true(#foo);

Perl adding corresponding/respective values of array to variables

I have some scalar values in array
#array=(1,2,3,4,5);
We can directly assign these values to variables as
($a,$b,$c,$d,$e)=#array;
Is there some way so that I can add the corresponding values of #array numbers like
$x +=10;
($a,$b,$c,$d,$e) +=#array;
Sorry for asking such silly question ;)
Try using map
my #array=(1,2,3,4,5);
my ($a,$b,$c,$d,$e) = map { $_ + 10 } #array;
You can sum all elements of array using sum from List::Util:
use List::Util qw(sum);
my $sum = sum(#array);
UPDATE:
It seems that you want to add arrays element by element, then you can use pairwise from List::Moreutils:
use List::MoreUtils qw(pairwise);
my #array = qw(10 20 30);
my #incr = qw( 1 2 3);
pairwise { $a += $b } #array, #incr; # (11,22,33)

Perl count repeated strings in array

I have an array that is like
my #array = ('cookies','balls','cookies','balls','balls');
but the real one is much bigger/longer.
How do I output the count of each repeated string in the array?
like in the example, cookies is 2 and balls is 3.
I think we can use
map {$count{$_}++;} #array;
instead of
foreach(#array)
{
unless(defined($count{$_}))
{
$count{$_} = 1;
}
else {
$count{$_}++;
}
}
to simplify the code.
"How do I output the count of each repeated string in the array?"
#!/usr/bin/perl
use strict;
use warnings;
my #array = ('cookies','balls','cookies','balls','balls', 'orphan');
my %count;
$count{$_}++ foreach #array;
#removing the lonely strings
while (my ($key, $value) = each(%count)) {
if ($value == 1) {
delete($count{$key});
}
}
#output the counts
while (my ($key, $value) = each(%count)) {
print "$key:$value\n";
}
Prints:
cookies:2
balls:3
Mind, that 'orphan' wasn't output.
Using Perl that's a little more idiomatic than some of the other answers...
use strict;
use warnings;
use 5.010;
my #array = ('cookies','balls','cookies','balls','balls');
my %count;
$count{$_}++ foreach #array;
say "$_: $count{$_}" foreach grep { $count{$_} != 1 } keys %count;
Try this more shorter code u will not get any thing shorter than this
my #array = ('cookies','balls','cookies','balls','balls');
my $hashh = {};
foreach (#array){
if(exists $hashh->{$_}){
$hashh->{$_}++;
} else{
$hashh->{$_} = 1;
}
}
print Dumper($hashh);

regex matching I think

First sorry if I should have added this to my earlier question today, but I now have the below code and am having problems getting things to add up to 100...
use strict;
use warnings;
my #arr = map {int( rand(49) + 1) } ( 1..100 ); # build an array of 100 random numbers between 1 and 49
my #count2;
foreach my $i (1..49) {
my #count = join(',', #arr) =~ m/,$i,/g; # ???
my $count1 = scalar(#count); # I want this $count1 to be the number of times each of the numbers($i) was found within the string/array.
# push(#count2, $count1 ." times for ". $i); # pushing a "number then text and a number / scalar, string, scalar" to an array.
push(#count2, [$count1, $i]);
}
#sort #count2 and print the top 7
my #sorted = sort { $b->[0] <=> $a->[0] } #count2;
my $sum = 0;
foreach my $i (0..$#sorted) { # (0..6)
printf "%d times for %d\n", $sorted[$i][0], $sorted[$i][1];
$sum += $sorted[$i][0]; # try to add up/sum all numbers in the first coloum to make sure they == 100
}
print "Generated $sum random numbers.\n"; # doesn't add up to 100, I think it is because of the regex and because the first number doesn't have a "," in front of it
# seem to be always 96 or 97, 93...
Replace these two lines:
my #count = join(',', #arr) =~ m/,$i,/g; # ???
my $count1 = scalar(#count); # I want this $count1 to be the number of times each of the numbers($i) was found within the string/array.
with this:
my $count1 = grep { $i == $_ } #arr;
grep will return a list of elements where only the expression in {} evaluates to true. This is less error-prone and much more efficient than joining the entire array and using a a regex. Also note that scalar is not necessary since the variable $count1 is scalar, so perl will return the result of grep in scalar context.
You can also get rid of this line:
push(#count2, $count1 ." times for ". $i); # pushing a "number then text and a number / scalar, string, scalar" to an array.
since you are already printing the same information in your last foreach loop.
#!/usr/bin/perl
use strict; use warnings;
use YAML;
my #arr;
$#arr = 99;
my %counts;
for my $i (0 .. 99) {
my $n = int(rand(49) + 1);
$arr[ $i ] = $n;
++$counts{ $n };
}
my #result = map [$_, $counts{$_}],
sort {$counts{$a} <=> $counts{$b} }
keys %counts;
my $sum;
$sum += $_->[1] for #result;
print "Number of draws: $sum\n";
You can probably reuse some well-tested code from List::MoreUtils.
use List::MoreUtils qw/ indexes /;
...
foreach my $i (1..49) {
my #indexes = indexes { $_ == $i } #arr;
my $count1 = scalar( #indexes );
push( #count2, [ $count1, $i ] );
}
If you don't need the warns in the sum loop, then I'd recommend using sum from List:Util.
use List::Util qw/ sum /;
...
my $sum = sum map { $_->[0] } #sorted;
If you insist on the loop, rewrite it as:
foreach my $i ( #sorted ) {

Resources