Hi PerlMonks people!

I have a rather tricky sort question that I've been trying to solve. In my array of hashes I've ID, Distance and RouteDistance. I want to sort on RouteDistance but this value is sometimes 0 and here comes the tricky part. When RouteDistance is 0 I want the sorting to be done on the Distance for this hash instead.

This example might make my problem clearer:

ID = 3, Distance = 2.3, RouteDistance = 4.3
ID = 2, Distance = 1.5, RouteDistance = 2.8
ID = 5, Distance = 2.1, RouteDistance = 0
ID = 1, Distance = 1.7, RouteDistance = 2.5

I want the sorting to be presented as this when the sorting is done:

ID = 1, Distance = 1.7, RouteDistance = 2.5
ID = 2, Distance = 1.5, RouteDistance = 2.8
ID = 5, Distance = 2.1, RouteDistance = 0
ID = 3, Distance = 2.3, RouteDistance = 4.3

Example code that I've been trying to make this work with...

From an XMLpost I'm receiving an array of hash references which im calling $xml_post_array{'data'}

In this first example I manage to sort by RouteDistance and in cases where RouteDistance is equal with another RouteDistance it will sort by Distance.
my @unsortedArray; foreach my $received_xml_post (@ {$xml_post_array{'data'}}) { push @unsortedArray, { 'ID' => $received_xml_post->{'ID'}, 'Distance => $received_xml_post->{'Dis +tance'}, 'RouteDistance' => $received_xml_post- +>{'RouteDistance'}}; } my @sortedArray = sort { $a->{RouteDistance} <=> $b->{RouteDistance || + $a->{Distance} <=> $b->{Distance} } @unsortedArray;
In the next example I want to get around the RouteDistance cases where RouteDistance is equal to 0.
my @unsortedArray; foreach my $received_xml_post (@ {$xml_post_array{'data'}}) { push @unsortedArray, { 'ID' => $received_xml_post->{'ID'}, 'Distance => $received_xml_post->{'Distance'} +, 'RouteDistance' => $received_xml_post->{'Rout +eDistance'}}; } my @sortedArray = sort { if( $a->{RouteDistance} == 0) {return -1} els +e {$a->{RouteDistance} <=> $b->{RouteDistance} $a->{Distance} <=> $b- +>{Distance} } @unsortedArray;

The 2nd code example will just sort everything on Distance. I've done an ugly version of this sorting that first sorts on Distance. Then I loop trough this and save current RouteDistance and in cases where RouteDinstance is equal to 0 I just pick up that previous RouteDistance and add 0.001 to it. However, this solution does not work with exreame cases and therefore I want to find out if there is a better way to handle this type of sorting. I'm sorry in advance for my English (not my native language). This is my first post here by the way so I might have done something wrong with this post but just let me know and I will now to next time.

Update 1: One of the suggestions that was close to achiveing this was this peace of code:

sort { $a->{routedistance} && $b->{routedistance} ? $a->{routedistance} <=> $b->{routedistance} : $a->{distance} <=> $b->{distance} } @ar;

unfortunately, this turned out to not work as I wanted when the RouteDistance was pre-ordered where one of the RouteDistance was zero.

I can add one thing to the table. The value of RouteDistance is not important, as long as it is sorted as I've described above. What I mean by this is that cases where RouteDistance is zero, this value can be replaces to something else, for example previous RouteDistance or nearest + 0.01 / -0.01. The value of RouteDistance will not be used to anything else then just sorting which makes this one option.

Update 2: The problem is not yet solved and and have no good solution for it.. :/

Update 3: The problem is solved. It might be some extreme cases where this all falls apart but with the tests I've done it works.

The solution:

#!/usr/bin/perl use warnings; use strict; my @unsorted_array = ( { ID => 3, Distance => 2.3, RouteDistance => + 4.3 }, { ID => 2, Distance => 1.5, RouteDistance => 2 +.8 }, { ID => 5, Distance => 2.1, RouteDistance => 0 + }, { ID => 1, Distance => 1.7, RouteDistance => 2 +.5 }); my @sorted = sort { if($a->{RouteDistance} == 0 || $b->{RouteDistance} == 0) { return ($a->{Distance} < $b->{Distance}) ? -1 : 1; } elsif ($a->{RouteDistance} == $b->{RouteDistance}) { return 0; } else { return ($a->{RouteDistance} < $b->{RouteDistance}) ? -1 : 1; } } @unsorted_array; foreach my $sorted_row (@sorted) { print "ID: ".$sorted_row->{'ID'}." Distance: ".$sorted_row->{'Dist +ance'}." RouteDistance: ".$sorted_row->{'RouteDistance'}."\n"; }

In reply to Sorting an array of hashes with an exception by martin_87

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.