use warnings; use strict; my %hash; while () { my @data = split; push @{ $hash{ $data[0] }{ $data[1] } } => "$data[2]: $data[3]"; } for my $key ( sort keys %hash ) { print $key, ' ', join( ', ' => map { $_, @{ $hash{$key}{$_} } } keys %{ $hash{$key} } ), $/; } __DATA__ id1 name1 cat1 catname1 id1 name1 cat2 catname2 id2 name2 cat3 catname3 id3 name3 cat1 catname1 id3 name3 cat4 catname4