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

Hello all,
I was wondering, if you have a file like the following:
jim 14 john 23 ernest 38 matilda 43 jim 34 ernest 27 john 44 matilda 22

how can you print
jim 14,34 john 23,44 ernest 38,27 matilda 43,22

I have been trying something with hashes, but didn't get me anywhere...

Replies are listed 'Best First'.
Re: How can I group lines in a file?
by zwon (Abbot) on May 16, 2009 at 17:54 UTC
    use strict; use warnings; my %res; while (<DATA>) { my ( $name, $weight ) = split /\s+/; push @{ $res{$name} }, $weight; } for ( sort keys %res ) { print "$_ ", join( ",", @{ $res{$_} } ), "\n"; } __DATA__ jim 14 john 23 ernest 38 matilda 43 jim 34 ernest 27 john 44 matilda 22
Re: How can I group lines in a file?
by BrowserUk (Patriarch) on May 17, 2009 at 00:30 UTC

    There's nothing wrong with the other solutions offered, but it can be done more concisely with no loss of clarity:

    my %HoA; ## Typo corrected open my $fh, '<', 'junk.dat' or die $!; /(\S+)\s+(\S+)/ and push @{ $HoA{ $1 } }, $2 while <$fh>; close $fh; pp \%HoA; { ernest => [38, 27], jim => [14, 34], john => [23, 44], matilda => [43, 22] }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Hi BrowserUk,

      I like your solution but I'm not sure I understand it properly. Why does this line work?
      /(\S+)\s+(\S+)/ and push @{ $HoA{ $1 } }, $2 while <$fh>;
      I broke the line into 3 parts (split, push and the reading the file) and I understand what each part does but what I don't understand is why it worked.

      For example, should I read the line from right to left? So, read the file then split "and" push??? Why does split and push combo work together. This is what I don't understand.

      If you can explain it or point me to the proper documentation, I would really appreciate it. Thanks
        split is a function, so there is no split. That code is equivalent to
        while( <$fh> ){ if(/(\S+)\s+(\S+)/){ push @{ $HoA{ $1 } }, $2; } }
        to test, add before the if
        warn 'scalar ', /(\S+)\s+(\S+)/; warn 'list ', join '|', /(\S+)\s+(\S+)/;
      Are you sure it works? It does if HoA is declared as a hash, not as a reference.

      Loss of clarity is kinda evident.

      It sure is understandable to a native speaker of Perl; but the fact that this is considered "clear" gives the language a bad name.

        It sure is understandable to a native speaker of Perl;

        It's Perl. You do have to understand Perl to understand it.

        but the fact that this is considered "clear" gives the language a bad name.
        • Does this combine a f b = (b >>=) . (return .) . f =<< a give Haskell a bad name?
        • How about this for Smalltalk?
          | s f | s := Prompter prompt: 'enter line'. f := Bag new. s do: [ :c | c isLetter ifTrue: [f add: c asLowercase] ]. ^f
        • Does this CH3(CH2)50CH3 give chemistry a bad name?
        • Or this y + ?y = ƒ(x+ ?x) = m (x + ?x) + c = m x + c + m ?x = y + m?x for mathematics?
        • Or this 1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Bxc6 dxc6 5. d3 Bb4+ 6. Nc3 Nf6 7. O-O Bxc3 chess?
        • Or this for physics?
        • Or this for choreography?
        • Or this musicians
        • ...

        Why do people expect Perl to look like Java or C and brand it with a '"bad name" when it doesn't?


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How can I group lines in a file?
by toolic (Bishop) on May 16, 2009 at 19:01 UTC
Re: How can I group lines in a file?
by johngg (Canon) on May 16, 2009 at 23:10 UTC

    zwon has pointed you in the right direction but I note that your output is not sorted by name but retains the order in which names first occured in the data. To keep that order you will need to preserve it in the hash and sort on it when printing.

    use strict; use warnings; my $order = 0; my %names = (); while( <DATA> ) { my( $name, $value ) = split; $names{ $name }->{ order } = ++ $order unless exists $names{ $name }; push @{ $names{ $name }->{ values } }, $value; } print do { local $" = q{,}; qq{$_ @{ $names{ $_ }->{ values } }\n}; } for sort { $names{ $a }->{ order } <=> $names{ $b }->{ order } } keys %names; __END__ jim 14 john 23 ernest 38 matilda 43 jim 34 ernest 27 john 44 matilda 22

    The output.

    jim 14,34 john 23,44 ernest 38,27 matilda 43,22

    I hope this is of interest.

    Cheers,

    JohnGG

      Just want to note that you can get the same order if you sort by name length or by first value ;)

      use strict; use warnings; my %res; while (<DATA>) { my ( $name, $weight ) = split /\s+/; push @{ $res{$name} }, $weight; } for ( sort { length($a) cmp length($b) } keys %res ) { print "$_ ", join( ",", @{ $res{$_} } ), "\n"; } for ( sort { $res{$a}->[0] <=> $res{$b}->[0] } keys %res ) { print "$_ ", join( ",", @{ $res{$_} } ), "\n"; } __DATA__ jim 14 john 23 ernest 38 matilda 43 jim 34 ernest 27 john 44 matilda 22
Re: How can I group lines in a file?
by bichonfrise74 (Vicar) on May 17, 2009 at 06:09 UTC
    oops, my bad. When I was writing my own version, I used "split" to get the values which is basically the regex thing that BrowserUk did. Also, my version is similar to what you just wrote... Read the file, get the values, push it in a hash of array.

    But BrowserUk's version was done in a single line which I'm still perplexed. Why does it work? (regex thing "and" pushing it in a hash of array).

    How does "and" work? How can it get the regex values and push it to HoA? Thanks.