3

I would like to sort this array based on the value after the comma

my @coords;
$coords[0] = "33.7645539, -84.3585973";
$coords[1] = "33.7683870, -84.3559850";
$coords[2] = "33.7687753, -84.3541355";

foreach my $coord (@sorted_coords) {
    print "$coord\n";
}

Output:

33.7687753, -84.3541355
33.7683870, -84.3559850
33.7645539, -84.3585973

I've thought about using map, grep, and capture groups as the list input for sort, but I haven't gotten very far:

my @sorted_coords = sort { $a <=> $b } map {$_ =~ /, (-*\d+\.\d+)/} @unique_coords;

2 Answers 2

7

It is easy to submit to the temptation to use a fancy implementation instead of something straightforward and clear. Unless the data set is huge, the speed advantage of using a transform is negligible, and comes at the cost of much reduced legibility

A standard sort block is all that's necessary here

use strict;
use warnings;

my @coords = (
    "33.7645539, -84.3585973",
    "33.7683870, -84.3559850",
    "33.7687753, -84.3541355",
);

my @sorted_coords = sort {
    my ($aa, $bb) = map { (split)[1] } $a, $b;
    $bb <=> $aa;
} @coords;


print "$_\n" for @sorted_coords;

output

33.7687753, -84.3541355
33.7683870, -84.3559850
33.7645539, -84.3585973



Update

If you prefer, the second field may be extracted from the input records using a regex instead. Replacing the map statement with something like this

my ($aa, $bb) = map /.*(\S+)/, $a, $b;

will work fine

Sign up to request clarification or add additional context in comments.

Comments

6

Looks like you could use a Schwartzian transform. You had the right idea:

my @coords;
$coords[1] = "33.7683870, -84.3559850";
$coords[2] = "33.7687753, -84.3541355";
$coords[0] = "33.7645539, -84.3585973";

my @sorted_coords = map  { $_->[0] }              # 3. extract the first element
                    sort { $b->[1] <=> $a->[1] }  # 2. sort on the second
                                                  #    element, descending
                    map  { [ $_, /,\s*(\S+)$/ ] } # 1. create list of array refs
                    @coords;

foreach my $coord (@sorted_coords) {
    print "$coord\n";
}

Edit: Adding Joshua's suggestion:

my @sorted_coords = map  { join ', ', @$_ }
                    sort { $b->[1] <=> $a->[1] }  
                    map  { [ split /, / ] }
                    @coords;

It seems easier to look at and more descriptive than my original example.

5 Comments

Thanks for linking the algorithm! I can use this idiom for other strings, not just this use case :P
The only thing I would change is to swap out the comparatively expensive regex capture with a split instead: map { [ $_, (split ',')[1] ] }
I prefer to always use // around the split RE, just to show that it is a regular expression, not just a string (e.g. split '|' does not split on pipes) (the special split ' ' being an exception)
@ysth: It also helps to contrast / / and ' ' as the PATTERN parameter, which behave very differently
careful when doing split/joins; split by default discards trailing fields, so your code will turn "1, 2, " into just "1, 2"; if you are going to want the original string, it is often better to just keep it around.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.