in reply to Re^2: Create sort function from a text file
in thread Create sort function from a text file
Rather than trying to modify each line to be compatible with a "cmp", alpha comparison, I split each line up into separate alpha and numeric tokens. cmp is used for alpha tokens and spaceship (<=>) is used for numeric values. That way, numeric 10 will sort higher than numeric 3. If for some reason, one value is "shorter" than the other and both are equal to that point, there is a "tie-breaker" so that the shortest one "wins".
The sort routine does a fair amount of 'work' to make the comparison. But with a 100 things or so, this "extra work" should make no significant performance difference. I did not make any assumptions as to the number of "parts" in each line. One example below shows a truncated line.
Upon further reflection and testing, this hasty idea below didn't work (shown in readmore tags instead of being deleted). The above code is better.use strict; use warnings; my @list = qw( R1-U10 R1-U1 R10-U11 R10-U1 R2-U2 R3-U13 R10-U5 R2 ); @list = sort special_compare @list; print "$_\n" for @list; sub special_compare { my (@myA) = $a =~ /([a-zA-z_]+|\d+)/g; my (@myB) = $b =~ /([a-zA-z_]+|\d+)/g; my $result=0; my $Atoken; my $Btoken; while ( defined ($Atoken = shift @myA) and defined ($Btoken = shif +t @myB) and $result == 0) { my $numeric = 0; $numeric = 1 if ($Atoken =~ /\d/ and $Btoken =~ /\d/); if ($numeric) { $result = ($Atoken <=> $Btoken); } else { $result = ($Atoken cmp $Btoken); } } if ($result ==0) #if one array "runs out", longest is "greater" { return -1 if (@myA < @myB); return 1 if (@myA > @myB); } return $result; } __END__ R1-U1 R1-U10 R2 R2-U2 R3-U13 R10-U1 R10-U5 R10-U11
Update again: As another thought, if the above generalized sort is not enough, rather than having the user's writing actual code, you could perhaps created some sort of simple grammar for the user to modify in a config file.
Perhaps:
A for Alpha field. N for numeric field. Those letters are really a "distinction without a difference" - mainly to keep your user's brain working correctly. What would matter are the 1,2,3,4 numbers (essentially indices into the @tokens array). Ignore A or N and do the sort automagically according to what the field actually is (just alpha or just numeric).Prototype: R10-U1 Fields: A1 N2 A3 N4 Sort Order: A3 N4 A1 N2
The above Order would put the units first. Rack order first would be same as field definition: A1 N2 A3 N4.
If you have some simple syntax like that in a config file, that is something that you could validate and have your special_compare() routine use. This avoids the problem of the "user writing code" - possibly with obscure syntax errors that they may not understand how to fix.. Not everybody understands Perl.
In some of my config files, I actually document some common scenarios as comments as a guide so the user doesn't have to actually "RTFM".
Anyway, just a thought. Many variations on this theme are possible. My advice is to not make it more complicated than it needs to be. I'd start with the general rack first sort above and then see how much demand there actually is for different sort orders.
I think the code, "does the right thing" with this goof ball example:
use strict; use warnings; my @list = qw( R1U1 R2U3 R10U1 R10-U1 R2-U2 R3-U13 R10-U5 R2 ); @list = sort special_compare @list; print "$_\n" for @list; sub special_compare { my (@myA) = $a =~ /([a-zA-z_]+|\d+)/g; my (@myB) = $b =~ /([a-zA-z_]+|\d+)/g; my $result=0; my $Atoken; my $Btoken; while ( defined ($Atoken = shift @myA) and defined ($Btoken = shif +t @myB) and $result == 0) { $result = ($Atoken cmp $Btoken); } if ($result ==0) #if one array "runs out", longest is "greater" { return -1 if (@myA < @myB); return 1 if (@myA > @myB); } return $result; } __END__ R1U1 R10U1 R10-U1 R10-U5 R2 R2-U2 R2U3 R3-U13
|
|---|