Ghost
Ghost

Reputation: 3966

Array manipulation in Perl

The Scenario is as follows:

I have a dynamically changing text file which I'm passing to a variable to capture a pattern that occurs throughout the file. It looks something like this:

my @array1; 
my $file = `cat <file_name>.txt`;
if (@array1 = ( $file =~ m/<pattern_match>/g) ) {
    print "@array1\n";
}

The array looks something like this:

10:38:49 788 56 51 56 61 56 59 56 51 56 80 56 83 56 50 45 42 45 50 45 50 45 43 45 54 10:38:51 788 56 51 56 61 56 59 56 51 56 80 56 83 56 50 45 42 45 50 45 50 45 43 45 54

From the above array1 output, the pattern of the array is something like this:

T1 P1 t1(1) t1(2)...t1(25) T2 P2 t2(1) t2(2)...t2(25) so on and so forth

Currently, /g in the regex returns a set of values that occur only twice (only because the txt file contains this pattern that number of times). This particular pattern occurrence will change depending on the file name that I plan to pass dynamically.

What I intend to acheive:

The final result should be a csv file that contains these values in the following format:

T1,P1,t1(1),t1(2),...,t1(25) 
T2,P2,t2(1),t2(2),...,t2(25) 
so on and so forth

For instance: My final CSV file should look like this:

10:38:49,788,56,51,56,61,56,59,56,51,56,80,56,83,56,50,45,42,45,50,45,50,45,43,45,54
10:38:51,788,56,51,56,61,56,59,56,51,56,80,56,83,56,50,45,42,45,50,45,50,45,43,45,54

The delimiter for this pattern is T1 which is time in the format \d\d:\d\d:\d\d

Example: 10:38:49, 10:38:51 etc

What I have tried so far:

use Data::Dumper; 
use List::MoreUtils qw(part);
my $partitions = 2;
my $i = 0;
print Dumper part {$partitions * $i++ / @array1} @array1;

In this particular case, my $partitions = 2; holds good since the pattern occurrence in the txt file is only twice, and hence, I'm splitting the array into two. However, as mentioned earlier, the pattern occurrence number keeps changing according to the txt file I use.

The Question:

How can I make this code more generic to achieve my final goal of splitting the array into multiple equal sized arrays without losing the contents of the original array, and then converting these mini-arrays into one single CSV file?

If there is any other workaround for this other than array manipulation, please do let me know.

Thanks in advance.

PS: I considered Hash of Hashes and Array of Hashes, but that kind of a data structure did not seem to be healthy solution for the problem I'm facing right now.

Upvotes: 1

Views: 125

Answers (1)

Borodin
Borodin

Reputation: 126732

As far as I can tell, all you need is splice, which will work fine as long as you know the record size and it's constant

The data you showed has 52 fields, but the description of it requires 27 fields per record. It looks like each line has T, P, and t1 .. t24, rather than ending at t25

Here's how it looks if I split the data into 26-element chunks

use strict;
use warnings 'all';

my @data = qw/
    10:38:49 788 56 51 56 61 56 59 56 51 56 80 56 83 56 50 45 42 45 50 45 50 45 43 45 54 10:38:51 788 56 51 56 61 56 59 56 51 56 80 56 83 56 50 45 42 45 50 45 50 45 43 45 54
/;

while ( @data ) {
    my @set = splice @data, 0, 26;
    print join(',', @set), "\n";
}

output

10:38:49,788,56,51,56,61,56,59,56,51,56,80,56,83,56,50,45,42,45,50,45,50,45,43,45,54
10:38:51,788,56,51,56,61,56,59,56,51,56,80,56,83,56,50,45,42,45,50,45,50,45,43,45,54

If you wanted to use List::MoreUtils instead of splice, the the natatime function returns an iterator that will do the same thing as the splice above

Like this

use List::MoreUtils qw/ natatime /;

my $iter = natatime 26, @data;

while ( my @set = $iter->() ) {
    print join(',', @set), "\n";
}

The output is identical to that of the program above

Note

It is very wrong to start a new shell process just to use cat to read a file. The standard method is to undefine the input record separator $/ like this

my $file = do {
    open my $fh, '<', '<file_name>.txt' or die "Unable to open file for input: $!";
    local $/;
    <$fh>;
};

Or if you prefer you could use File::Slurper like this

use File::Slurper qw/ read_binary /;

my $file = read_binary '<file_name>.txt';

although you will probably have to install it as it is not a core module

Upvotes: 3

Related Questions