Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

sort array by date

by gowthamtr (Sexton)
on Jan 03, 2007 at 10:04 UTC ( #592728=perlquestion: print w/ replies, xml ) Need Help??
gowthamtr has asked for the wisdom of the Perl Monks concerning the following question:

I have an array with elements as follows

Apr 2006
Dec 2007

FY means Fisal year.

I want the output to be as follows

Apr 2006
Dec 2007

Can u suggest how to approach the problem???

Comment on sort array by date
Re: sort array by date
by shonorio (Hermit) on Jan 03, 2007 at 10:20 UTC
    I think you can use Date::Manip, take a look of "sort date". But first, maybe, you have to translate your FY?? to a valid date with DateTime::Fiscal::Year.

    Solli Moreira Honorio
    Sao Paulo - Brazil

Re: sort array by date
by shigetsu (Hermit) on Jan 03, 2007 at 10:42 UTC
Re: sort array by date
by BrowserUk (Pope) on Jan 03, 2007 at 11:02 UTC

    Assuming your examples are definitive, this GRT should work for you:




    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanx for the solution...It is working perfectly...but i cudnot understand the code from the link abt Advanced sorting you mentioned....

      Can u tell me some other link to understand the code in a better way???

        There are lots of links around, many of them linked from the node I linked to, but if you do not understand the explanations there, the others may also leave you cold.

        I'll have a go at explaining them, but I doubt I will do much better than others.

        Simple sorting using sort

        The basic way of sorting in perl is to use the built in sort function.

        1. my @sortedAlpha = sort @alphaData;

          This pretty much exactly what you'd expect. Takes an array(*) of data, orders it ascending (smallest first) according the ordinal (ASCII**) values of the characters in the strings:


          *actually a list, but ignore that for now.


        2. my @reversedSordedAlpha = sort{ $b cmp $a } @alphaData;

          Here we want the data order in descending (largest first) sequence, so we have(*) to use code block to alter the default ordering.

          The { $b cmp $a } but of that tells sort that we wish to perform the ordering ourselves instead of using the default as we did in 1 above (which is equivalent to { $a cmp $b }). This says that for each pair of values in the input, Perl passes them to us (as $a and $b), and we perform a comparison and return

          • -1 if $a is less than $b
          • 1 if $a is greater than $b
          • 0 if $a equals $b

          With this information, sort can correctly do what it needs to do to reorder the input appropriately.

          By using the built-in cmp function that is specifically designed for comparing strings and producing these values, we achieve the result we are after.

          Supplying the arguments $a & $b to the function in reverse order has the effect of reverseing the sort.

          However, if we use cmp on numeric data, we do not get the effect we want:


          For numeric data we need to use the other built-in comparison operator <=>.

          *we can also use reverse, and that may actually be quicker, but this is a tutorial and this way demostrates the point I wish to make.

        3. my @reverseSortedNumbers = sort { $b <=> $a } @numbers;

          Using <=> produces the desired result.


          The difference is that <=> compares the (binary) numerical value of it's arguments, instead of the bytewise string comparisons of the ASCII representation that cmp does.

        4. my @sortedNumbers = sort{ $a <=> $b } @numbers;

          And for an ascending numerical sort, we just pass the parameters to <=> in the 'right' order.

        How sort actually works doesn't really matter. That it is about as fast as you are likely to get, is built-in to langauge so always available and understands Perl internals does.

        So, sorting data when you want to sort according the the entire value of each element of the array or list is easy. It gets a little more complex when you want to sort the elements by a sub field of each element.



        This works fine, but each element will be passed to the comparison block multiple times, and that means we are extracting the sort field from each element multiple times, which can slow things down. For more complex cases, like your dates problem, this could be significant. The purposes of the "advanced sorting" mechanisms is to reduce this slowdown by performing the extraction of the significant data from each input element once only.

        The ST

        One mechanism, and possibly the simplest to understand, it merlyn's ST (Schwartzian Transform). This works by pre-processing the elements to extract the fields and package them into anonymous arrays. The list of annonymous arrays are then sorted and finally the original elements are recovered from the list of sorted anonymous arrays and stored into the sorted array.



        @sorted = map{ $_->[1] } sort{ $a->[0] <=> $b->[0] } map{ [ substr( $_, 1 ), $_ ] } qw[ A473 B659 C123 D222 E001 ];; print Dumper @sorted;; VAR1 = 'E001'; $VAR2 = 'C123'; $VAR3 = 'D222'; $VAR4 = 'A473'; $VAR5 = + 'B659';

        This is identical to the previous example, but more compact. It can also be formatted as a single line, though that will usually mean that it extends beyond the boundary at which PM does it's dumb wrapping. It can also be laid out in several different ways to that I've shown, but I find that this makes the three signifcant steps of the algorithm clearly delineated, by indenting the contents of the code blocks just as you would for any other block of code. Eg. The body of if and while statements etc. I like this consistency.



        Here we've ordered the data according to the numeric field as before, but this time we've used the alpha field to 'tiebreak' values when the numeric values are the same. It's easy to see how this can be further extended to handle as many fields of any type we need. This simple, obvious extension mechanism is the great strength of the ST.

        The caveats


        It's also the case that calling back into Perl code via the sort comparison block imposes a significant time cost. (Don't worry about this for the simple cases of sort{ $a <=> } ... and sort{ $b cmp $a } shown above, as Perl has optimisations to recognise these simple cases, and it doesn't actually do a callback at all).


        The GRT



        print for map{ ## Chop off the bit we added. substr( $_, 3 ) } sort map{ ## Note: No comparison block callback. ## Extract the field as before, but concatenate it with the origin +al element ## instead of building an anonymous array containing both elements +. substr( $_, 1 ) . $_ } qw[ A473 B659 C123 D222 E001 ];; E001 C123 D222 A473 B659
        A cheat

        This is actually something of a cheat of a GRT. It only works because in my carefully constructed example data, the numeric fields all have the same number of digits, with smaller values having leading zeros. Whilst this works fine, and makes the algorithm easy to follow (try adding print statements inside the code blocks to follow what is going on), real life data is rarely so accommodating. Whilst you could use sprintf to add leading zeros and so make your ascii encoded numeric fields sort correctly using the string comparisons (cmp), it turns out that there is a better (faster) way.


        So, using pack, the previous example becomes:


        That may not look so very different from the previous version, but the big advantage is that it will handle any integer values correctly, whereas with the previous version, you need to know how big the largest numeric field is, so that you can add the correct number of leading zeroes.



        The pattern

        Hopefully, the pattern is becoming obvious. Use pack with appropriate templates 'N', 'n', 'd' etc. to prepend the sort field data to the strings in the order or precedence. Then use unpack and the same template as you used for the sort fields, but wrapped in 'x[...]' to strip the prefix off and recover the original elements.

        Hmmm. Is that any clearer than the existing explanations?

        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://592728]
Approved by ysth
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
As of 2014-12-27 18:44 GMT
Find Nodes?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?

    Results (177 votes), past polls