I have a list of strings with the following format:
('group1-1', 'group1-2','group1-9', 'group2-1','group2-2', 'group2-9','group1-10', 'group2-10' )
I need them to be sorted as below:
group wise first and then number wise.
('group1-1', 'group1-2','group1-9','group1-10', 'group2-1','group2-2', 'group2-9', 'group2-10' )
I've written following code, but it's not working as expected: a comparator that sorts based on the group and if the groups match, it sorts based on the number.
my @list = ('group1-1', 'group1-2','group1-9',
'group2-1','group2-2', 'group2-9','group1-10', 'group2-10' );
@list = sort compare @list;
for (@list){
print($_."\n");
}
sub compare{
my $first_group, $first_num = get_details($a);
my $second_group, $second_num = get_details($b);
if($first_group < $second_group){
return -1;
} elsif($first_group == $second_group){
if ( $first_num < $second_num) {
return -1;
} elsif ( $first_num == $second_num ) {
return 0;
} else {
return 1;
}
} else{
return 1;
}
}
sub get_details($){
my $str= shift;
my $group = (split /-/, $str)[0];
$group =~ s/\D//g;
my $num = (split /-/, $str)[1];
$num =~ s/\D//g;
return $group, $num;
}
You could use a Schwartzian transform:
use warnings;
use strict;
my @list = ('group1-1', 'group1-2','group1-9',
'group2-1','group2-2', 'group2-9','group1-10', 'group2-10' );
@list = map { $_->[0] }
sort { $a->[1] cmp $b->[1] or $a->[2] <=> $b->[2] }
map { [$_, split /-/] }
@list;
for (@list) {
print($_."\n");
}
Prints:
group1-1
group1-2
group1-9
group1-10
group2-1
group2-2
group2-9
group2-10
There's a little detail with the data here that can lead to a quiet bug. When you use the pre-hyphen substring for sorting (group1 etc), it has both letters and numbers so when sorted lexicographically it may be wrong for multi-digit numbers. For example
group1, group2, group10
is sort-ed (by default cmp) into
group1 group10 group2
What is wrong, I presume.
So inside sorting we need to break the groupN into group and N, and sort numerically by N.
use warnings;
use strict;
use feature 'say';
my @list = ('group1-1', 'group1-2','group1-9',
'group2-1','group2-2', 'group2-9',
'group1-10', 'group2-10',
'group10-2', 'group10-1' # Added some 'group10' data
);
# Break input string into: group N - N (and sort by first then second number)
@list =
map { $_->[0] }
sort { $a->[2] <=> $b->[2] or $a->[4] <=> $b->[4] }
map { [ $_, /[0-9]+|[a-zA-Z]+|\-/g ] }
@list;
say for @list;
The regex extracts both numbers and words from the string, for sorting. But if that lone substring is always indeed the same (group) then we only ever sort by numbers and can use /[0-9]+/g, and compare numerically arrayref elements at indices 1 and 2.
Prints
group1-1 group1-2 group1-9 group1-10 group2-1 group2-2 group2-9 group2-10 group10-1 group10-2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With