http://www.perlmonks.org?node_id=907929

qwerty155 has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

My first question here, thanks in advance for any help you can give me. I'm looking to re-sort a text string (easily split to an array if req'd) that is currently in this format:

$string = "(x1,x1)(x2,y2)........(xn,yn)"

..and sorted by the x co-ordinate, then by y. How an I reorder the string by y co-ordinate first, then by x (without swapping the x & y round within the brackets - or at least swapping back if need be)?

Many thanks

Replies are listed 'Best First'.
Re: (Hopefully) simple sorting question
by Corion (Patriarch) on Jun 03, 2011 at 08:18 UTC
Re: (Hopefully) simple sorting question
by AnomalousMonk (Archbishop) on Jun 03, 2011 at 11:33 UTC
    $string = "(x1,x1)(x2,y2)........(xn,yn)"

    A real, sortable string together with its expected sorted output form would have been helpful, both to give the monks something to work with and to clarify the problem. (The apparently erroneous  (x1,x1) first term also doesn't help.)

    ... sorted by the x co-ordinate, then by y. ... reorder the string by y co-ordinate first, then by x ...

    I'm confused by this as to whether you want to sort "by x then by y", or "by y then by x". I will assume the former.

    ... sorted by the x co-ordinate ...

    This suggests to me a Update: ascending numeric sort.

    Given these assumptions, here's an example of the sort of thing explained in Corion's link.

    >perl -wMstrict -le "my $u = '(x1,y9)(x11,y8)(x1,y7)(x11,y6)(x2,y5)(x22,y4)(x2,y3)(x22,y2)'; ;; my $s = join '', map { $_->[0] } sort { $a->[1][0] <=> $b->[1][0] || $a->[1][1] <=> $b->[1][1] } map { [ $_, [ $_ =~ m{\d+}xmsg ]] } split m{ (?<= \)) (?= \() }xms, $u ; ;; print qq{'$s'}; " '(x1,y7)(x1,y9)(x2,y3)(x2,y5)(x11,y6)(x11,y8)(x22,y2)(x22,y4)'

    Note: The original string must be split into an array because sort only works on arrays.

    Update: Here's a version that will be significantly faster if you're sorting lotsa strings or very long strings. See A Fresh Look at Efficient Perl Sorting for a discussion.

    >perl -wMstrict -le "my $u = '(x1,y9)(x11,y8)(x1,y7)(x11,y6)(x2,y5)(x22,y4)(x2,y3)(x22,y2)'; ;; use constant WIDTH => 10; my $s = join '', map { substr $_, WIDTH+WIDTH } sort map { sprintf '%0*4$d%0*4$d%s', m{\d+}xmsg, $_, WIDTH } split m{ (?<= \)) (?= \() }xms, $u ; ;; print qq{'$s'}; " '(x1,y7)(x1,y9)(x2,y3)(x2,y5)(x11,y6)(x11,y8)(x22,y2)(x22,y4)'
Re: (Hopefully) simple sorting question
by qwerty155 (Initiate) on Jun 03, 2011 at 08:40 UTC

    Sorry, you're right. Sometimes it's hard to find the answer when you're not sure what you're actually looking for (and a newbie).

    Thanks for the swift reply.

Re: (Hopefully) simple sorting question
by Anonymous Monk on Jun 03, 2011 at 09:01 UTC
    <EffectivnessDetective Rating="mildly amusing">
      <Title IsNotEffective EffectivnessScore="-2">
       <HasStopWord> simple </HasStopWord>
       <HasStopWord> question </HasStopWord>
       <HasStopWord> hopefully </HasStopWord>
       <HasGoWord> sorting </HasGoWord>
       <AutoSuggested>
         <AutoTitle> sorting coordinates first by x then by y </AutoTitle>
         <RelatedNode id="544435"> Sorting large sets of geometric coordinates </Node>
        </AutoSuggested>
      </Title>
      <Question IsEffective HasReceivedAnswer IsFaq EffectivnessScore="2">
        <HasCodeTags />
        <HasCoherentQuestion />
        <DoesNotHaveRealInput />
        <DoesNotHaveRealOutput />
        <DoesNotHaveExpectedOutput />
        <DoesNotHaveRealExpectedOutputDelta />
      </Question>
    </EffectivnessDetective>
      Code tags would have been nice. :-)

      XML Sucks.

      I reckon we are the only monastery ever to have a dungeon stuffed with 16,000 zombies.
Re: (Hopefully) simple sorting question
by sundialsvc4 (Abbot) on Jun 03, 2011 at 11:47 UTC

    Briefly, your sort-comparison function will consider x first, and then consider y only when the values of x are equal.   You also may need to do a little magic (e.g. “<” vs.lt”) to be certain that the comparison being performed is, in fact, a numeric one.   Test thoroughly.