Reputation: 45
I'am a beginner in learning perl.
What I'm trying to do here is to split the array @value
and insert it into a new array. my problem is, I do not know exactly how to make my coding to run in loop and get my desired result.
Is it possible to get the desired result using this method or is there any other alternative/way to get the same result?
My code is as below;
my @separated = ();
my @separated1 = ();
my @separated2 = ();
my @separated3 = ();
my $counter = 0;
my @values = "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD";
foreach (@values) {
my @separated = split(' ', $_);
push @separated1, $separated[0];
push @separated2, $separated[1];
push @separated3, $separated[2];
}
$counter++
print "separated1 = @separated1\n";
print "separated2 = @separated2\n";
print "separated3 = @separated3\n";
Result that I got;
separated1 = aaa
separated2 = 111
separated3 = AAA
Desired Result;
separated1 = aaa bbb ccc ddd
separated2 = 111 222 333 444
separated3 = AAA BB CCC DD
Upvotes: 2
Views: 3200
Reputation: 66873
A rare ocassion where the C-style for loop is suitable, to iterate over every 3rd element
my $string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
my (@sep1, @sep2, @sep3);
my @values = split ' ', $string;
for (my $i=0; $i <= $#values; $i += 3) {
push @sep1, $values[$i];
push @sep2, $values[$i+1];
push @sep3, $values[$i+2];
}
This assumes that the array indeed has all triplets, or better check each element.
But it's normally far nicer to work with a single structure than with a set of parallel arrays. For example, use an array with elements that are array references
use Data::Dump qw(dd);
my @sep;
for (my $i=0; $i <= $#values; $i += 3) {
for my $j (0..2) {
push @{$sep[$j]}, $values[$i+$j];
}
}
dd \@sep;
where the double iteration can be avoided with a much cleaner
for my $i (0..$#values) {
push @{$sep[$i%3]}, $values[$i]
}
which replaces the two loops.
This prints
[ ["aaa", "bbb", "ccc", "ddd"], [111, 222, 333, 444], ["AAA", "BBB", "CCC", "DDD"], ]
I use Data::Dump to see complex data. An alternative in the core is Data::Dumper.
And then there are many modules with all kinds of utility routines for work with lists.
For example, using part from List::MoreUtils to partition the @values
array
my @sep = map { [ @values[@$_] ] } part { $_%3 } 0..$#values;
This produces the same @sep
with arrayrefs as above.
The part
returns a list of arrayrefs, each containing indices since it partitioned the list of indices of @values
. Then in map each arrayref is evaluated into its list of indices (@$_
), which is used to take the corresponding slice of @values
; that list is used to make an arrayref with []
. So map
returns a list of arrayrefs, with values partitioned as needed.
For working with references please see tutorial perlreftut and reference perlref
Upvotes: 4
Reputation: 6378
I like the solutions from @ikegami and @zdim. @zdim's use of part()
from List::MoreUtils
reminded me of natatime
:
my @values = split(' ', "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD");
use List::MoreUtils 'natatime';
my $nata_iter = natatime 3, @values ;
my @aoa ;
while (my @tmp = $nata_iter->()) { push @aoa, \@tmp; };
Not really a consideration but possibly of interest: by using a temporary array (@tmp
) to store the output of the iterator, the original @values
remains intact whereas the more straightforward splice()
is destructive.
Upvotes: 1
Reputation: 27723
This expression might help you to get your desired results:
([a-z]+\s)([0-9]+\s)([A-Z]+)
It has three capturing groups for each of your desired results. You can add or reduce boundaries to it, as you wish, and it might be faster than other methods.
This graph shows how the expression would work and you can visualize other expressions in this link:
const regex = /([a-z]+\s)([0-9]+\s)([A-Z]+)/gm;
const str = `aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD`;
const subst = `\n$1 & $2 & $3\n`;
// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);
console.log('Substitution result: ', result);
You can simply use $1
, $2
and $3
and separate your data:
use strict;
my $str = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
my $regex = qr/([a-z]+\s)([0-9]+\s)([A-Z]+)/mp;
my $subst = '';
my $result = $str =~ s/$regex/$subst/rg;
print "The result of the substitution is' $result\n";
This JavaScript snippet shows the performance of that expression using a simple 1-million times for
loop.
const repeat = 1000000;
const start = Date.now();
for (var i = repeat; i >= 0; i--) {
const string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
const regex = /([a-z]+\s)([0-9]+\s)([A-Z]+)/gm;
var match = string.replace(regex, "$1");
}
const end = Date.now() - start;
console.log("YAAAY! \"" + match + "\" is a match 💚💚💚 ");
console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. 😳 ");
Upvotes: 2
Reputation: 9231
Another job for List::UtilsBy:
use strict;
use warnings;
use List::UtilsBy 'bundle_by', 'unzip_by';
my $string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
my @vals = split ' ', $string;
my ($sep1, $sep2, $sep3) = unzip_by { @$_ } bundle_by { [@_] } 3, @vals;
print "sep1: @$sep1\nsep2: @$sep2\nsep3: @$sep3\n";
Upvotes: 3
Reputation: 52334
Another version using part
from the non-core but very useful List::MoreUtils module, that partitions up the elements directly:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say state/;
use List::MoreUtils qw/part/;
my $str = "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD";
my ($sep1, $sep2, $sep3) = part { state $i = 0; $i++ % 3 } split(' ', $str);
say "sep1: @$sep1";
say "sep2: @$sep2";
say "sep3: @$sep3";
prints out
sep1: aaa bbb ccc ddd
sep2: 111 222 333 444
sep3: AAA BBB CCC DDD
The magic here is with state, which ends up creating a variable local to the block it's in that retains its value across multiple evaluations of the block.
Upvotes: 3
Reputation: 385565
my ( @foos, @bars, @quxs );
my @values = split(' ', $input);
while (@values) {
push @foos, shift(@values);
push @bars, shift(@values);
push @quxs, shift(@values);
}
The above can also be written as follows:
my ( @foos, @bars, @quxs );
for ( my @values = split(' ', $input); @values; ) {
push @foos, shift(@values);
push @bars, shift(@values);
push @quxs, shift(@values);
}
Are you sure you want parallel arrays, though? While they can save memory, it's usually hard to work with them, and more error-prone. In an object-dominated landscape, they are virtually never seen.
You could use an AoA:
my @moos;
my @values = split(' ', $input);
while (@values) {
push @moos, [ splice(@values, 0, 3) ];
}
You could use an AoH:
my @moos;
my @values = split(' ', $input);
while (@values) {
my %moo; @moo{qw( foo bar qux )} = splice(@values, 0, 3);
push @moos, \%moo;
}
Upvotes: 4