in reply to Re: Reorganizing the content of a file
in thread Reorganizing the content of a file

Amazing: Your output is sorted by id - alphabetically - and still gives the same ordering as sorting by names :)
  • Comment on Re^2: Reorganizing the content of a file

Replies are listed 'Best First'.
Re^3: Reorganizing the content of a file
by Laurent_R (Canon) on Dec 18, 2018 at 19:00 UTC
    Yeah, right, lucky that the data happened to be like that. I really did not try very hard to sort, as the OP did not specify anything about that, I just included a sort on the keys to tidy up the output, and it turned out to be exactly the output sample provided in the OP.
      I really liked your solution. It simple, clear and easy to understand.
      It is not part of my original request but is it possible to change the sorting of the ids, so it will keep the same order, without depending on the lexicography order?
      If so, what change in code should I do?
      The only thing that comes to my mind, is to add a third field to each id that will represent the index. I don't care about the sibling's order, but it will be nice to see an order of parent-chlid.
      For example, if we have the following input:
      5,#,A 1,5,B 4,5,C 2,4,D
      The output should be:
      A B,A C,A D,C,A
      and not the following output:
      B,A D,C,A C,A A
      Please note that it does not matter for me the order of the sibling.
      Thank you for your help! :)

        Hmm, a requirements change :)

        #!/usr/bin/perl # https://perlmonks.org/?node_id=1227383 use strict; use warnings; local $_ = join '', <DATA>; for my $id ( /^(\d+)/gm ) { print /^$id,.*,(\S+)/m; $id = $1, print ',', /^$id,.*,(\S+)/m while /^$id,(\d+)/m; print "\n"; } __DATA__ 5,#,A 1,5,B 4,5,C 2,4,D
        Hi ovedpo15,

        The only problem to keep the original order of the input is that the data structure used in my solution, a hash, does not have a defined order. However, the solution is fairly easy: we only need to keep track of the original order one way or another.

        One possible way to do that is to add an array (@ranks) as an auxiliary data structure when reading the file and then to loop over the array when preparing the output:

        use strict; use warnings; use feature 'say'; my (%tree, @ranks); while (<DATA>) { chomp; my ($id, $pid, $name) = split /,/; $tree{$id} = {pid => $pid, name => $name}; push @ranks, $id; } for my $id (@ranks) { my @parent_list; my $temp_id = $id; while (exists $tree{$temp_id}) { push @parent_list, $tree{$temp_id}{name}; $temp_id = $tree{$temp_id}{pid}; } say join ",", @parent_list; } __DATA__ 15,10,name3 10,#,name1 12,10,name2 5,12,name4 8,5,name5
        which produces the following output:
        $ perl test_ranks.pl name3,name1 name1 name2,name1 name4,name2,name1 name5,name4,name2,name1
        The solution you contemplated in your question is also relatively easy, except for the syntax used to sort the records according to the ranks, which may be regarded as a bit complicated for a Perl beginner. Here, I have added a rank field into the records and then sort the id's in accordance to the rank:
        use strict; use warnings; use feature 'say'; my (%tree); while (<DATA>) { chomp; my ($id, $pid, $name) = split /,/; $tree{$id} = {pid => $pid, name => $name, rank => $.}; } for my $id (sort { $tree{$a}{rank} <=> $tree{$b}{rank} } keys %tree) { my @parent_list; my $temp_id = $id; while (exists $tree{$temp_id}) { push @parent_list, $tree{$temp_id}{name}; $temp_id = $tree{$temp_id}{pid}; } say join ",", @parent_list; } __DATA__ 15,10,name3 10,#,name1 12,10,name2 5,12,name4 8,5,name5
        which produces the same output as before.

        Another possible way might be to read the input file again to get the id's in the proper order, but I wouldn't recommend that as that would be less efficient.

        You may also take a look at the Hash::Ordered module on the CPAN, which implements a hash data structure with a builtin order.

        Update: Fixed a typo: s/reparing the output/preparing the output/.