Need to retain the order of array of hash references.

Discussion in 'Perl Misc' started by alwaysonnet, Nov 1, 2006.

  1. alwaysonnet

    alwaysonnet Guest

    hi all -- Help needed

    I'm trying to sort an array of hash-references based on the "A/C No" by
    preserving the "Type" order,'ZCurrency' and 'Status' , where "A/C No"
    is unique for every record.

    Important Note is that - if Records having same "Type","Status" and
    "ZCurrency" have different "A/C No" so sorting them with "A/C No"
    doesn't effect the actual order of display.

    1) I'm looping through every element, checking current record if
    "Type","Status" and "ZCurrency" is equal to previous record. if not,
    pushing it to final array.

    2) if current record matches with previous record, i'm pushing to
    another array and sorting them with the "A/C No". Because, both the
    records will have same "Type","Status" and "ZCurrency".

    3) By this, I got two arrays @final and @curr ... need to merge them
    into one by sorting with "A/C No" and preserving the order with
    original but sort with "A/C No" if the records have same
    "Type","Status" and "ZCurrency".

    Partially working code ---

    #!/usr/bin/perl

    my %type_order = qw/Prefund 1 Receipt 2 Payment 3/;

    my @records = (
    {'Type'=>'Prefund' , 'A/C No'=>12345 , 'Status'=>'Y',
    'ZCurrency' =>'GBP'},
    {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y',
    'ZCurrency' =>'SEK'},
    {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y',
    'ZCurrency' =>'SEK'},
    {'Type'=>'Prefund' , 'A/C No'=>32222 , 'Status'=>'N',
    'ZCurrency' =>'SEK'},
    {'Type'=>'Receipt' , 'A/C No'=>32365 , 'Status'=>'Y',
    'ZCurrency' =>'EUR'},
    {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N',
    'ZCurrency' =>'AIR'},
    {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N',
    'ZCurrency' =>'AIR'},
    {'Type'=>'Receipt' , 'A/C No'=>64237 , 'Status'=>'N',
    'ZCurrency' =>'GBP'},
    {'Type'=>'Payment' , 'A/C No'=>22476 , 'Status'=>'Y',
    'ZCurrency' =>'AUS'},
    {'Type'=>'Payment' , 'A/C No'=>22447 , 'Status'=>'Y',
    'ZCurrency' =>'BEL'},
    {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N',
    'ZCurrency' =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N',
    'ZCurrency' =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N',
    'ZCurrency' =>'EUR'},
    );

    foreach $current (@records) {
    if ($previous->{'Type'} eq $current->{'Type'} && $previous->{'Status'}
    eq $current->{'Status'} && $previous->{'ZCurrency'} eq
    $current->{'ZCurrency'}) {
    push(@curr,$previous);
    push(@curr,$current);
    } else {
    push(@final,$current);
    }
    $previous = $current;
    }

    %seen = ();
    foreach $item (@curr) {
    push(@uniq, $item) unless $seen{$item}++;
    }

    @uniq = sort { $type_order{$a->{'Type'}} <=> $type_order{$b->{'Type'}}
    ||
    $a->{'A/C No'} <=> $b->{'A/C No'}

    } @uniq;

    foreach $x(@uniq) {
    print " $x->{'Type'} $x->{'A/C No'} $x->{'Status'}
    $x->{'ZCurrency'}\n";
    }

    I want my final output as

    {'Type'=>'Prefund' , 'A/C No'=>12345 , 'Status'=>'Y', 'ZCurrency'
    =>'GBP'},
    {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y', 'ZCurrency'
    =>'SEK'},
    {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y', 'ZCurrency'
    =>'SEK'},
    {'Type'=>'Prefund' , 'A/C No'=>32222 , 'Status'=>'N', 'ZCurrency'
    =>'SEK'},
    {'Type'=>'Receipt' , 'A/C No'=>32365 , 'Status'=>'Y', 'ZCurrency'
    =>'EUR'},
    {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N', 'ZCurrency'
    =>'AIR'},
    {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N', 'ZCurrency'
    =>'AIR'},
    {'Type'=>'Receipt' , 'A/C No'=>64237 , 'Status'=>'N', 'ZCurrency'
    =>'GBP'},
    {'Type'=>'Payment' , 'A/C No'=>22476 , 'Status'=>'Y', 'ZCurrency'
    =>'AUS'},
    {'Type'=>'Payment' , 'A/C No'=>22447 , 'Status'=>'Y', 'ZCurrency'
    =>'BEL'},
    {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},

    So records to be effected are

    {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y', 'ZCurrency'
    =>'SEK'},
    {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y', 'ZCurrency'
    =>'SEK'},

    {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N', 'ZCurrency'
    =>'AIR'},
    {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N', 'ZCurrency'
    =>'AIR'},

    {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},
    {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N', 'ZCurrency'
    =>'EUR'},

    Thanks,
    Raj
     
    alwaysonnet, Nov 1, 2006
    #1
    1. Advertising

  2. alwaysonnet

    Jim Gibson Guest

    In article <>,
    alwaysonnet <> wrote:

    > hi all -- Help needed


    I am afraid I do not understand exactly what it is you are trying to
    accomplish. Perhaps you can clarify some of your confusing and
    contradictory statements.

    >
    > I'm trying to sort an array of hash-references based on the "A/C No" by
    > preserving the "Type" order,'ZCurrency' and 'Status' , where "A/C No"
    > is unique for every record.


    If 'A/C No' values are unique, then you can do a simple sort on 'A/C
    No'. There is no need to retain the order, because the order of the
    sorted array is completely determined by the values of 'A/C No'.
    Unfortunately, your sample output below is NOT sorted by 'A/C No'.

    >
    > Important Note is that - if Records having same "Type","Status" and
    > "ZCurrency" have different "A/C No" so sorting them with "A/C No"
    > doesn't effect the actual order of display.


    This sentence doesn't make any sense, and in fact may not be
    grammatically correct. Did you mean "if ... then ..." instead of "if
    .... so ..."?

    If two or more records have the same 'Type', 'Status', and 'ZCurrency'
    values and different 'A/C No' values, then sorting them by 'A/C No' can
    definitely affect their order in the final result.

    >
    > 1) I'm looping through every element, checking current record if
    > "Type","Status" and "ZCurrency" is equal to previous record. if not,
    > pushing it to final array.


    You should be using Perl's sort function to sort your records.

    >
    > 2) if current record matches with previous record, i'm pushing to
    > another array and sorting them with the "A/C No". Because, both the
    > records will have same "Type","Status" and "ZCurrency".


    Are records with the same 'Type', 'Status', and 'ZCurrency' values
    always continguous in your set of records?

    >
    > 3) By this, I got two arrays @final and @curr ... need to merge them
    > into one by sorting with "A/C No" and preserving the order with
    > original but sort with "A/C No" if the records have same
    > "Type","Status" and "ZCurrency".


    One general technique for preserving order in a sort is to add an
    extra, temporary field that contains a sequence number defined however
    you wish with unique value for each record. You can then use this field
    as your final sort key to be used when the other sort keys are
    identical. However, current versions of Perl use an order-preserving
    sort algorithm, so if you do your sorting in one step this shouldn't be
    necessary.

    >
    > Partially working code ---
    >
    > #!/usr/bin/perl
    >
    > my %type_order = qw/Prefund 1 Receipt 2 Payment 3/;
    >
    > my @records = (
    > {'Type'=>'Prefund' , 'A/C No'=>12345 , 'Status'=>'Y',
    > 'ZCurrency' =>'GBP'},
    > {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y',
    > 'ZCurrency' =>'SEK'},
    > {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y',
    > 'ZCurrency' =>'SEK'},
    > {'Type'=>'Prefund' , 'A/C No'=>32222 , 'Status'=>'N',
    > 'ZCurrency' =>'SEK'},
    > {'Type'=>'Receipt' , 'A/C No'=>32365 , 'Status'=>'Y',
    > 'ZCurrency' =>'EUR'},
    > {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N',
    > 'ZCurrency' =>'AIR'},
    > {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N',
    > 'ZCurrency' =>'AIR'},
    > {'Type'=>'Receipt' , 'A/C No'=>64237 , 'Status'=>'N',
    > 'ZCurrency' =>'GBP'},
    > {'Type'=>'Payment' , 'A/C No'=>22476 , 'Status'=>'Y',
    > 'ZCurrency' =>'AUS'},
    > {'Type'=>'Payment' , 'A/C No'=>22447 , 'Status'=>'Y',
    > 'ZCurrency' =>'BEL'},
    > {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N',
    > 'ZCurrency' =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N',
    > 'ZCurrency' =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N',
    > 'ZCurrency' =>'EUR'},
    > );
    >
    > foreach $current (@records) {
    > if ($previous->{'Type'} eq $current->{'Type'} && $previous->{'Status'}
    > eq $current->{'Status'} && $previous->{'ZCurrency'} eq
    > $current->{'ZCurrency'}) {
    > push(@curr,$previous);
    > push(@curr,$current);
    > } else {
    > push(@final,$current);
    > }
    > $previous = $current;
    > }
    >
    > %seen = ();
    > foreach $item (@curr) {
    > push(@uniq, $item) unless $seen{$item}++;
    > }
    >
    > @uniq = sort { $type_order{$a->{'Type'}} <=> $type_order{$b->{'Type'}}
    > ||
    > $a->{'A/C No'} <=> $b->{'A/C No'}
    >
    > } @uniq;
    >
    > foreach $x(@uniq) {
    > print " $x->{'Type'} $x->{'A/C No'} $x->{'Status'}
    > $x->{'ZCurrency'}\n";
    > }
    >
    > I want my final output as
    >
    > {'Type'=>'Prefund' , 'A/C No'=>12345 , 'Status'=>'Y', 'ZCurrency'
    > =>'GBP'},
    > {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y', 'ZCurrency'
    > =>'SEK'},
    > {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y', 'ZCurrency'
    > =>'SEK'},
    > {'Type'=>'Prefund' , 'A/C No'=>32222 , 'Status'=>'N', 'ZCurrency'
    > =>'SEK'},
    > {'Type'=>'Receipt' , 'A/C No'=>32365 , 'Status'=>'Y', 'ZCurrency'
    > =>'EUR'},
    > {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N', 'ZCurrency'
    > =>'AIR'},
    > {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N', 'ZCurrency'
    > =>'AIR'},
    > {'Type'=>'Receipt' , 'A/C No'=>64237 , 'Status'=>'N', 'ZCurrency'
    > =>'GBP'},
    > {'Type'=>'Payment' , 'A/C No'=>22476 , 'Status'=>'Y', 'ZCurrency'
    > =>'AUS'},
    > {'Type'=>'Payment' , 'A/C No'=>22447 , 'Status'=>'Y', 'ZCurrency'
    > =>'BEL'},
    > {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},


    It looks like you want to sort by: 1) 'Type', 2) 'Status', 3)
    'ZCurrency', and 4) 'A/C No'. Is that correct? Try this (untested):

    @sorted = sort { $a->{'Type'} cmp $b->{'Type'} ||
    $a->{'Status'} cmp $b->{'Status'} ||
    $a->{'ZCurrency'} cmp $b->{'ZCurrency'} ||
    $a->{'A/C No'} cmp $b->{'A/C No'} } @records;

    >
    > So records to be effected are
    >
    > {'Type'=>'Prefund' , 'A/C No'=>45678 , 'Status'=>'Y', 'ZCurrency'
    > =>'SEK'},
    > {'Type'=>'Prefund' , 'A/C No'=>33333 , 'Status'=>'Y', 'ZCurrency'
    > =>'SEK'},
    >
    > {'Type'=>'Receipt' , 'A/C No'=>78878 , 'Status'=>'N', 'ZCurrency'
    > =>'AIR'},
    > {'Type'=>'Receipt' , 'A/C No'=>32435 , 'Status'=>'N', 'ZCurrency'
    > =>'AIR'},
    >
    > {'Type'=>'Payment' , 'A/C No'=>56546 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>44444 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},
    > {'Type'=>'Payment' , 'A/C No'=>43434 , 'Status'=>'N', 'ZCurrency'
    > =>'EUR'},


    You can help us help you by writing a complete, short-as-possible,
    working program that demonstrates the problem you are having. In your
    case, I would recommend testing with fewer and shorter data records
    until you get the sort part working. Something link:

    @records = (
    { a => 1, b => 1, c => 1, d => 1 },
    { a => 1, b => 1, c => 1, d => 2 },
    { a => 1, b => 1, c => 1, d => 3 },

    (you get the picture, I hope).

    Good luck.
     
    Jim Gibson, Nov 1, 2006
    #2
    1. Advertising

  3. alwaysonnet

    alwaysonnet Guest

    May be i'm weak in conveying my requirement... here we go...

    > If 'A/C No' values are unique, then you can do a simple sort on 'A/C
    > No'. There is no need to retain the order, because the order of the
    > sorted array is completely determined by the values of 'A/C No'.
    > Unfortunately, your sample output below is NOT sorted by 'A/C No'.
    >
    >

    If 'A/C No' values are unique, then you can do a simple sort on 'A/C
    No'. There is no need to retain the order, because the order of the
    sorted array is completely determined by the values of 'A/C No'.
    Unfortunately, your sample output below is NOT sorted by 'A/C No'.

    'A/C No' is unique for all the records, but the records must be
    displayed in contiguous order of prefund,receipt and payment.

    For Ex- If there are 3 - Prefund , 4- Receipt and 2- Payment records,
    then all the records must be displayed as

    Prefund
    Prefund
    Prefund
    Receipt
    Receipt
    Receipt
    Receipt
    Payment
    Payment

    So ,sorting with 'A/C No' will effect the order of "Type" which I dont
    want. that's why i declared an hash of type_order.

    > If two or more records have the same 'Type', 'Status', and 'ZCurrency'
    > values and different 'A/C No' values, then sorting them by 'A/C No' can
    > definitely affect their order in the final result.
    >


    I agree with you that if two records with same "Type",'Status' and
    'ZCurrency' then sorting them with A/C No will definitely effect their
    result.

    > >
    > > 2) if current record matches with previous record, i'm pushing to
    > > another array and sorting them with the "A/C No". Because, both the
    > > records will have same "Type","Status" and "ZCurrency".

    >
    > Are records with the same 'Type', 'Status', and 'ZCurrency' values
    > always continguous in your set of records?
    >


    Yes, if records are having same "Type","Status" and "ZCurrency" , they
    are always contiguous but their "A/C No" are not in sorted order.

    so , my concern is to sort the contigous records having same
    "Type","Status" and "ZCurrency" with their "A/C No"'s

    > It looks like you want to sort by: 1) 'Type', 2) 'Status', 3)
    > 'ZCurrency', and 4) 'A/C No'. Is that correct? Try this (untested):
    >
    > @sorted = sort { $a->{'Type'} cmp $b->{'Type'} ||
    > $a->{'Status'} cmp $b->{'Status'} ||
    > $a->{'ZCurrency'} cmp $b->{'ZCurrency'} ||
    > $a->{'A/C No'} cmp $b->{'A/C No'} } @records;
    >
    > You can help us help you by writing a complete, short-as-possible,
    > working program that demonstrates the problem you are having. In your
    > case, I would recommend testing with fewer and shorter data records
    > until you get the sort part working. Something link:
    >
    > @records = (
    > { a => 1, b => 1, c => 1, d => 1 },
    > { a => 1, b => 1, c => 1, d => 2 },
    > { a => 1, b => 1, c => 1, d => 3 },
    >
    > (you get the picture, I hope).
    >
    > Good luck.


    Thanks,
    Raj
     
    alwaysonnet, Nov 2, 2006
    #3
  4. alwaysonnet

    alwaysonnet Guest

    May be i'm weak in conveying my requirement... here we go...


    > If 'A/C No' values are unique, then you can do a simple sort on 'A/C
    > No'. There is no need to retain the order, because the order of the
    > sorted array is completely determined by the values of 'A/C No'.
    > Unfortunately, your sample output below is NOT sorted by 'A/C No'.


    'A/C No' is unique for all the records, but the records must be
    displayed in contiguous order of prefund,receipt and payment.

    For Ex- If there are 3 - Prefund , 4- Receipt and 2- Payment records,
    then all the records must be displayed as

    Prefund
    Prefund
    Prefund
    Receipt
    Receipt
    Receipt
    Receipt
    Payment
    Payment

    So ,sorting with 'A/C No' will effect the order of "Type" which I dont
    want. that's why i declared an hash of type_order.

    > If two or more records have the same 'Type', 'Status', and 'ZCurrency'
    > values and different 'A/C No' values, then sorting them by 'A/C No' can
    > definitely affect their order in the final result.


    I agree with you that if two records with same "Type",'Status' and
    'ZCurrency' then sorting them with A/C No will definitely effect their
    result.

    > > 2) if current record matches with previous record, i'm pushing to
    > > another array and sorting them with the "A/C No". Because, both the
    > > records will have same "Type","Status" and "ZCurrency".


    > Are records with the same 'Type', 'Status', and 'ZCurrency' values
    > always continguous in your set of records?


    Yes, if records are having same "Type","Status" and "ZCurrency" , they
    are always contiguous but their "A/C No" are not in sorted order.

    so , my concern is to sort the contigous records having same
    "Type","Status" and "ZCurrency" with their "A/C No"'s

    > It looks like you want to sort by: 1) 'Type', 2) 'Status', 3)
    > 'ZCurrency', and 4) 'A/C No'. Is that correct? Try this (untested):


    > @sorted = sort { $a->{'Type'} cmp $b->{'Type'} ||
    > $a->{'Status'} cmp $b->{'Status'} ||
    > $a->{'ZCurrency'} cmp $b->{'ZCurrency'} ||
    > $a->{'A/C No'} cmp $b->{'A/C No'} } @records;



    > You can help us help you by writing a complete, short-as-possible,
    > working program that demonstrates the problem you are having. In your
    > case, I would recommend testing with fewer and shorter data records
    > until you get the sort part working. Something link:



    > @records = (
    > { a => 1, b => 1, c => 1, d => 1 },
    > { a => 1, b => 1, c => 1, d => 2 },
    > { a => 1, b => 1, c => 1, d => 3 },



    > (you get the picture, I hope).



    > Good luck.


    Thanks,
    Raj
     
    alwaysonnet, Nov 2, 2006
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. rp
    Replies:
    1
    Views:
    538
    red floyd
    Nov 10, 2011
  2. Thies C.  Arntzen
    Replies:
    20
    Views:
    279
    Hal Fulton
    Aug 13, 2006
  3. Srijayanth Sridhar
    Replies:
    19
    Views:
    627
    David A. Black
    Jul 2, 2008
  4. Arvin Portlock
    Replies:
    6
    Views:
    141
    Arvin Portlock
    Sep 2, 2005
  5. Replies:
    11
    Views:
    189
    Eric Schwartz
    Oct 10, 2005
Loading...

Share This Page