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

I'm populating, for display, an AoA with a unordered list of serial numbers. I need to group by prefix putting the last read group at the top of the list. The 'X' indicates the prefix read. "num" in the first column is there to display mysteriously added elements.

Display based on __DATA__ SNO A B C D num: 1011 X X X X num: 0001 X X X X num: 0011 X X X X num: 0201 X X X X num: num: ...

Based on the data given, I should get just the 4 items as shown, but for some reason, each time I attempt to update the AoA, an empty cell gets added. I've included a couple of print Dumper() lines where the problem occurs, but the reason it's occuring eludes me.

Can someone explain what's going on. (and if you know a better way to do this, I'm all ears\eyes)

use strict; use Data::Dumper; my @display = (); # AoA while (<DATA>) { build_display($_); } print " SNO A B C D\n"; for (@display) { print 'num: ' . join(' ', @$_) . "\n"; } print "\n"; ########################## sub build_display { ########################## my $serialno = shift; chomp $serialno; if ($serialno) { my ($prefix,$number) = ($serialno =~ /^(\w)(\w{4})/); my $j = ($prefix =~ /A/i) ? 1 : ($prefix =~ /B/i) ? 2 : ($prefix =~ /C/i) ? 3 : ($prefix =~ /D/i) ? 4 : 0; my $temp = ''; for (0..$#display) { if ($display[$_][0] =~ /^$number$/i) { ## Need to update +array_ref $temp = $display[$_]; ## copy to temp splice(@display, $_, 1); ## delete from array. Added +to top later. print "After Delete: " . Dumper(\@display); <stdin>; } } ## ?? Adds an element to the @display array if update is required. But + why OR how ?? print "Before Add: " . Dumper(\@display); <stdin>; ## if 'number' not in array, create a new entry. $temp = [$number,' ',' ',' ',' '] if (!$temp); $temp->[$j] = 'X'; ## flag the applicable prefix unshift @display, $temp; ## add to top of array. } } __DATA__ A0011 B0001 C0201 D0001 A1011 B0201 C1011 D0201 A0201 B0011 C0011 D0011 A0001 B1011 C0001 D1011

Replies are listed 'Best First'.
Re: Populate and sort AoA
by Tanktalus (Canon) on Nov 14, 2005 at 22:52 UTC

    In your for (0..$#display) loop, you check $display[$#display][0] to see if it matches something. The problem is, you may have already spliced an element of the array away, and so the array is now smaller.

    For example, you may have an array of 4 elements ($#display == 3). Then you find an element again, so you splice it out, so the array is now 3 elements. However, your loop still is "0..$#display" which was "0..3" at the time that perl evaluated it. So you get back to evaluating $display[3][0] - and perl needs to autovivify $display[3] in order to evaluate $display[3][0]. That is the problem.

    Solution 1 is to add a "last" right after the splice. Once you've found the serial number, you're not going to find another one - the purpose of this loop is to keep things unique. So there can't be another one - just get out, and you won't evaluate past the end of the array anymore.

    Solution 2 is to move to HoH's. In this world, I would have a structure like this:

    %data = ( serial_number => { A => 0 or 1, B => 0 or 1, # etc. order_stamp => number }, # etc. );
    Then, I'd have an order number outside the loop, and each time I touch a serial number, I'd also assign $data{$serial}{order_stamp} = ++$order. Then, to get the desired order, just do a reverse sort on the order_stamp:
    @serial_order = reverse sort { $data{$a}{order_stamp} <=> $data{$b}{ +order_stamp} } keys %data;
    But if you're using AoA's for space reasons, well, you'll have to live with scanning for your desired serial number (sounds slow to me ;->). Note that rather than having A, B, C, D as separate keys, you can just put them in a 'data => []' key to get them all quickly. Up to you.

Re: Populate and sort AoA
by GrandFather (Saint) on Nov 14, 2005 at 22:46 UTC

    The two lines:

    for (0..$#display) { ... splice(@display, $_, 1); ## delete from array. Added to top later. ... }

    Don't play well together. Use a while loop instead. The splice is altering the number of elements in @display so unfortunate things are likely to happen.


    Perl is Huffman encoded by design.
Re: Populate and sort AoA
by Errto (Vicar) on Nov 14, 2005 at 23:08 UTC
    The problem is that in your for loop you are removing elements from the array, but your loop will continue iterating until $#display, the size of the array when your loop began. Then within the loop, you are accessing $display[$_][0], which will cause $display[$_] to spring into existence and contain an array reference as its value, even if there is currently no element at index $_. My suggestion is to just use the array reference directly instead of splicing it out and back in:
    my $row; for my $temp (@display) { if ($temp->[0] =~ /^$number$/i) { $row = $temp; } } unless ($row) { $row = [$number,' ',' ',' ',' ']; push @display, $row; } $row->[$j] = 'X';

      I thought about that solution, too, but I believe it would change the output - if nedals was going through all that trouble just to reorder things, there probably was a reason. I tried very hard to come up with a solution that would produce the same output as nedals had worked towards.

        There is indeed a reason I go to all that trouble.

        The serial numbers are recieved in random order, up to 20K of them. I want the 'operater' to know what were the last 20 serial numbers received (last-in at the top of the list) and what's still missing from the 'group, A-D'. There a section of the script that reads from a database to get serial-number groups that may have been read much earlier (no longer on the last 20 list).

        I could simply get the display data directly from the database, but I concluded that selecting and sorting from 20K records would be time consuming so I decided to maintain a 'last-20' array (or hash) instead.

        Tanktalus, I tried out your hash idea and got it working. I'm not sure which is better. Capturing the serial numbers is easier, but the display gets pretty messy.

        I guess it's a toss up :)

Re: Populate and sort AoA
by nedals (Deacon) on Nov 14, 2005 at 23:27 UTC

    Thanks.

    Errto, your explanation of what's going on was a great help

    Tanktalus, your solution 1, add a 'last', solved the problem. I'll also try out your HoH soultion.