This is an archived low-energy page for bots and other anonmyous visitors.
Please sign up if you are a human and want to interact.
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
Re: Populate and sort AoA
by GrandFather (Saint) on Nov 14, 2005 at 17:46 UTC
|
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.
| [reply] [d/l] |
Re: Populate and sort AoA
by Tanktalus (Canon) on Nov 14, 2005 at 17: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. | [reply] [d/l] [select] |
Re: Populate and sort AoA
by Errto (Vicar) on Nov 14, 2005 at 18: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';
| [reply] [d/l] [select] |
|
|
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.
| [reply] |
|
|
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 :)
| [reply] |
|
|
|
|
Re: Populate and sort AoA
by nedals (Deacon) on Nov 14, 2005 at 18: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.
| [reply] |
|
|