use strict; use warnings; use Data::Dumper; my (%posts, @threads, $id, $parent, $title); while () { chomp; ### It's easy to add more fields here if you want: ($id, $parent, $title) = split / /, $_, 3; my %post = ('id' => $id, 'title' => $title); $posts{$id} = \%post; if ($id == $parent) { push @threads, \%post; } else { push @{$posts{$parent}{'children'}}, \%post; } } construct($_) for @threads; sub construct { my $p = $_[0]; print "$p->{'id'} $p->{'title'}\n"; construct($_) for @{$p->{'children'}}; } __DATA__ 1 1 Title 2 1 Re: Title 3 1 Re: Title 4 2 Re: Re: Title 5 5 Title 2 6 5 Re: Title 2 7 4 Re: Re: Re: Title