Arengin has asked for the wisdom of the Perl Monks concerning the following question:

Hi
I was wondering how to alternate the return of an array.
As from another question I have the following code:

#!/usr/bin/perl use strict; use warnings; my %data; my $cur_tag; open (FILE, '<', $ARGV[0]) or die "Could not open file: $!"; while (<FILE>) { chomp; if ( my ($tag,$line) = /^(X\S+)\s+(.+)$/ ) { $data{$tag} .= "\n" if exists $data{$tag}; $data{$tag} .= $line; $cur_tag = $tag; } elsif ($cur_tag && !/^BEGIN_TAG/) { $data{$cur_tag} .= " NEWLINE ".$_; } } for my $tag (sort keys %data) { print "$tag: $data{$tag}\n"; }
With the input file like
BEGIN_TAG X1 test test test X2 no no no no X3 yes yes yes BEGIN_TAG X1 test test test tes test test X2 no no no no no nono non no no nononono no no no X3 hi hi hi hi hi hi hi hi hi hi hi hi
And the return
X1: test test test test test test NEWLINE tes test test X2: no no no no no no no no no NEWLINE nono non no no NEWLINE nononono no no no X3: yes yes yes hi hi hi hi hi hi NEWLINE hi hi hi hi NEWLINE hi hi
Meaning all X1, all X2 and than all X3

However now I was wondering how I can change that to get

First X1, first X2, first X3
second X1, second X2, second X3


like

X1;X2;X3
X1;X2;X3

Any ideas? I tried to play around with the sort of the data array but that didn't do it at all.

Thanks for any ideas.

Arengin

Replies are listed 'Best First'.
Re: Returning an array in a specific way for csv
by hippo (Archbishop) on Mar 13, 2017 at 16:01 UTC
    I tried to play around with the sort of the data array

    %data is not an array. Rather, it is a hash which is quite a different structure and does not retain a natural order. See the FAQ entry How do I sort a hash (optionally by value instead of key)?

    The rest of your question as stated is not the clearest and so it would be pure guesswork to try to propose a solution. Can you be more specific?

Re: Returning an array in a specific way for csv
by haukex (Archbishop) on Mar 13, 2017 at 15:54 UTC

    Could you be more specific what you want the output to look like? Also, how would the code know when to break apart the input - is it just each "X1" marker that the code encounters, or is it something else like "BEGIN_TAG"? To put that a different way, what should the output for this input look like? (How do I post a question effectively?)

    BEGIN_TAG X1 a b c d X1 e f g h BEGIN_TAG X1 i j k l X1 m n o p

    As for the data structure, as one possibility I'd suggest you have a look at "Hashes of Arrays" in perldsc.

    Update: For CSV input and output, I'd very strongly recommend Text::CSV.

      I need it to be like
      test test test; no no no no; yes yes yes test test testtes test test; no no no no no nono non no nono nonono no + no no; hi hi hi hi hi hi hi hi hi hi hi hi
      Like
      X1; X2; X3
      X1; X2; X3

      The BEGIN_TAG is the recuring tag that splits the output lines.

        That doesn't answer my question as to what the output for the input I showed should look like.

        #!/usr/bin/env perl use warnings; use strict; my @data; my %tags; my $cur_tag; while (<DATA>) { chomp; if (/^BEGIN_TAG/) { push @data, {} } elsif ( my ($tag,$line) = /^(X\S+)\s+(.+)$/ ) { $data[-1]{$tag} .= " NewRec " if exists $data[-1]{$tag}; $data[-1]{$tag} .= $line; $tags{$tag}++; $cur_tag = $tag; } elsif ($cur_tag) { $data[-1]{$cur_tag} .= " NewLine ".$_; } } use Text::CSV; my $csv = Text::CSV->new({binary=>1,auto_diag=>2,eol=>"\n", sep_char=>";",always_quote=>1,blank_is_undef=>1}); $csv->print(select, [sort keys %tags]); for my $row (@data) { my @vals = map { $row->{$_} } sort keys %tags; $csv->print(select, \@vals); } __DATA__ BEGIN_TAG X1 a b X2 one two X4 foo X1 c d X3 test BEGIN_TAG X1 e f g h X2 three four X4 bar quz baz X2 five six

        Output

        "X1";"X2";"X3";"X4" "a b NewRec c d";"one two";"test";"foo" "e f NewLine g h";"three four NewRec five NewLine six";;"bar NewLine q +uz baz"
Re: Returning an array in a specific way for csv
by tybalt89 (Monsignor) on Mar 13, 2017 at 21:19 UTC

    Like so?

    #!/usr/bin/perl # http://perlmonks.org/?node_id=1184431 use strict; use warnings; for ( split /^BEGIN_TAG\nX\d+ /m, do { local $/; <DATA> } ) { s/\n */ /g; # combine lines s/ X\d+/;/g; # change Xn to ; s/\s*\z/\n/; # make sure \n at end of line print; } __DATA__ BEGIN_TAG X1 test test test X2 no no no no X3 yes yes yes BEGIN_TAG X1 test test test tes test test X2 no no no no no nono non no no nononono no no no X3 hi hi hi hi hi hi hi hi hi hi hi hi

    ignoring what seems to be a typo in the output you show.

      Interesting way to do it and yes the result is what I was looking for. Thank you very much.