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

Hello monks... I have a file with several lines like:
GEORGE 21
GEORGE 45
NICK 12
PETER 27
JIM 18
JIM 87
CHRIS 33
Is there a quick way to print:
GEORGE 21
45
NICK 12
PETER 27
JIM 18
87
CHRIS 33
I want to show only the unique names and, if a name has multiple numbers, show each number in a separate line without repeating the name at the beginning.

Replies are listed 'Best First'.
Re: Any hints on how to do this?
by johngg (Canon) on Jul 25, 2006 at 22:24 UTC
    Assuming that your names are always grouped this should work. It also assumes that names and numbers are of the length and magnitude shown in the OP, but adjustments to the sprintf() would be simple. Here it is

    use strict; use warnings; my %seen = (); print map {sprintf qq{%-7s%2d\n}, @$_} map {[$seen{$_->[0]} ++ ? q{} : $_->[0], $_->[1]]} map {[split]} <DATA>; __END__ GEORGE 21 GEORGE 45 NICK 12 PETER 27 JIM 18 JIM 87 CHRIS 33

    and when run, it produces

    GEORGE 21 45 NICK 12 PETER 27 JIM 18 87 CHRIS 33

    If your names were not already grouped you could insert a sort like this

    print map {sprintf qq{%-7s%2d\n}, @$_} map {[$seen{$_->[0]} ++ ? q{} : $_->[0], $_->[1]]} sort {$a->[0] cmp $b->[0]} map {[split]} <DATA>;

    I hope this is of use.

    Cheers,

    JohnGG

Re: Any hints on how to do this?
by ahmad (Hermit) on Jul 25, 2006 at 21:49 UTC

    Try this , it might give you an idea ...

    my %HASH; while (<DATA>) { my ($name , $value ) = split(/\s+/,$_); if ( exists( $HASH{$name} ) ) { $HASH{$name} .= ",$value"; }else{ $HASH{$name} = $value; } } foreach my $name (keys %HASH) { print "Name : $name and values : $HASH{$name}\n"; } __DATA__ GEORGE 21 GEORGE 45 NICK 12 PETER 27 JIM 18 JIM 87 CHRIS 33
Re: Any hints on how to do this?
by CountZero (Bishop) on Jul 25, 2006 at 21:39 UTC
    Provided the names are grouped together, you could save the name in a variable and check the name in the next line. If it equals the saved name, just print some spaces, otherwise print the name.

    There, did that solve your homework? ;-)

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

      Hi CountZero, thanks for your time... That was exactly what I was thinking of doing, but I don't seem to be getting anywhere.. Do I need an array or just check the name in the following line, perhaps using the <> to move to the following line and do my pattern matching again?
        No need to use an array:
        use strict; my ($saved_name, $number) = split /\s/, <DATA>; print "$saved_name\t$number\n"; while (<DATA>) { (my $name, $number) = split /\s+/, $_; if ($saved_name eq $name) { print ' '; } else { print $name; } print "\t$number\n"; $saved_name=$name; } __DATA__ GEORGE 21 GEORGE 45 NICK 12 PETER 27 JIM 18 JIM 87 CHRIS 33
        Output:
        GEORGE 21 45 NICK 12 PETER 27 JIM 18 87 CHRIS 33

        CountZero

        "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

Re: Any hints on how to do this?
by Ieronim (Friar) on Jul 25, 2006 at 22:18 UTC
    The whole processing can be done by one command :)
    #!/usr/bin/perl use warnings; use strict; my $str = <<PAIRS; GEORGE 21 GEORGE 45 NICK 12 PETER 27 JIM 18 JIM 87 CHRIS 33 PAIRS my $name =''; $str = join "\n\n", map { sprintf "%-8s %s", ($_->[0] ne $name ? $nam +e = $_->[0] : ''), $_->[1]} map { [split " ", $_, 2] } split /\n+/, $ +str; print $str;
    It prints:
    GEORGE 21 45 NICK 12 PETER 27 JIM 18 87 CHRIS 33
    The snippet assumes that the lines containing the same name follow each other. It clears the name only if it is equal to one in previous line.

         s;;Just-me-not-h-Ni-m-P-Ni-lm-I-ar-O-Ni;;tr?IerONim-?HAcker ?d;print
Re: Any hints on how to do this?
by Util (Priest) on Jul 26, 2006 at 02:43 UTC
    The OP asked for "a quick way", so here is my one-liner:
    perl -ape 'substr($_,0,length $l) =~ tr/ / /c if $F[0] eq $l;$l=$F[0]' + <<END GEORGE 21 GEORGE 45 NICK 12 PETER 27 JIM 18 JIM 87 CHRIS 33 END
    prints:
    GEORGE 21 45 NICK 12 PETER 27 JIM 18 87 CHRIS 33
    On review, I see that my solution is practically the same as CountZero's $saved_name algorithm, but needs no pre-calculated size for the 'name' field.
Re: Any hints on how to do this?
by Anonymous Monk on Jul 26, 2006 at 06:05 UTC
    Many thanks to all of you!