push-ing each "organ" to an autovivified anonymous array keyed by its "animal" allows preservation of the original order of "organs" as found in the file (if this is of any importance). If preserving original order isn't important, use the simpler two-level hash approach described by others.
c:\@Work\Perl\monks>perl -wMstrict -le
"use autodie;
no autodie qw(open close);
;;
use List::MoreUtils qw(uniq);
;;
use Data::Dump qw(dd);
;;
my $file = qq{bird beak\n}
. qq{bird beak\n}
. qq{bird claw\n}
. qq{bird wings\n}
. qq{bird feathers\n}
. qq{snake fangs\n}
. qq{snake scales\n}
. qq{snake fangs\n}
. qq{snake tail\n}
;
print qq{[[$file]]};
;;
open my $fh, '<', \$file or die qq{opening ram file: $!};
;;
my %hash;
while (my $line = <$fh>) {
my $parsed =
my ($animal, $organ) =
$line =~ m{ \A ([[:alpha:]]+) \s+ ([[:alpha:]]+) \Z }xmsg;
;;
die qq{bad line '$line'} unless $parsed;
;;
push @{ $hash{$animal} }, $organ;
}
;;
close $fh or die qq{closing ram file: $!};
;;
@$_ = uniq @$_ for values %hash;
dd \%hash;
"
[[bird beak
bird beak
bird claw
bird wings
bird feathers
snake fangs
snake scales
snake fangs
snake tail
]]
{
bird => ["beak", "claw", "wings", "feathers"],
snake => ["fangs", "scales", "tail"],
}
Give a man a fish: <%-{-{-{-<