Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Sort AoH by another array

by saberworks (Curate)
on Nov 06, 2007 at 00:34 UTC ( [id://649121]=perlquestion: print w/replies, xml ) Need Help??

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

Hello, I came up with the following code (simplified example) and I'm wondering if you can think of a better way to do it. Grepping through the entire array once for each element in the first list seems incredibly wasteful, but I'm at a loss as to how to fix it. Basically, the first array is the order in which the user selected something. The Array of Hashes is the order in which stuff is pulled out of the database. I need to make sure the Array of Hashes is put in the same order the user selected the items.

Please note that it's just a coincidence that the first array is 1..5 in order, it's possible it could be in any other order.

Thanks in advance for any help!
#!/usr/bin/perl use warnings; use strict; use Data::Dumper; my $array_ref = [1, 2, 3, 4, 5]; my $array_of_hashes_ref = [ {key => 4, name => 'label4'}, {key => 2, name => 'label2'}, {key => 5, name => 'label5'}, {key => 1, name => 'label1'}, {key => 3, name => 'label3'} ]; # WANT: # $array_of_hashes_ref = [ # {key => 1, name => 'label1'}, # {key => 2, name => 'label2'}, # {key => 3, name => 'label3'} # {key => 4, name => 'label4'}, # {key => 5, name => 'label5'}, # ]; my $new_array_ref; for my $key (@$array_ref) { push @$new_array_ref, grep {$_->{'key'} == $key} @$array_of_hashes +_ref; } warn Dumper($new_array_ref);

Replies are listed 'Best First'.
Re: Sort AoH by another array
by erroneousBollock (Curate) on Nov 06, 2007 at 00:54 UTC
    First, I'll paraphrase you:

    my $key_ordering = [8, 1, 5, 3, 9]; my $data = [ {key => 1, name => 'label4'}, {key => 3, name => 'label2'}, {key => 5, name => 'label5'}, {key => 8, name => 'label1'}, {key => 9, name => 'label3'} ];
    Secondly, note that to work every 'key' of each tuple in @$data must be present in @$key_ordering.

    my %lookup = map { $_->{key} => $_->{name} } @$data; my @ordered = map { {key => $_, name => $lookup{$_}} } @$key_ordering;

    Which just suggests to me that $data was the wrong type of data-structure to begin with. If you really need it like that, you might consider maintaining (in parallel) a lookup table mapping 'key' to indexes into @$data.

    -David

      Thanks, I agree about the data structure. It's generated from an SQL query with an IN () clause. The results have to be ordered in the order the user selected the items (using a web interface). I don't think there's a way to do that directly with an SQL ORDER BY clause. I will use the suggestions here for a lookup hash, thanks again.
        The results have to be ordered in the order the user selected the items (using a web interface). I don't think there's a way to do that directly with an SQL ORDER BY clause.

        I thought that too, and then I discovered this:

        SELECT * FROM table ORDER BY FIELD( foo, 4, 2, 5, 1, 3 );

        I'm not sure which versions of MySQL it works in, but definitely in 5.

        Note there's a bug where you can't have a space between FIELD and the bracket.



        Nobody says perl looks like line-noise any more
        kids today don't know what line-noise IS ...
Re: Sort AoH by another array
by GrandFather (Saint) on Nov 06, 2007 at 00:56 UTC

    Build a lookup hash:

    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my $array_ref = [1, 2, 3, 4, 5]; my $array_of_hashes_ref = [ {key => 4, name => 'label4'}, {key => 2, name => 'label2'}, {key => 5, name => 'label5'}, {key => 1, name => 'label1'}, {key => 3, name => 'label3'} ]; my %lookup; @lookup{map {$_->{key}} @$array_of_hashes_ref} = @$array_of_hashes_ref +; print "$lookup{$_}{key}, $lookup{$_}{name}\n" for @$array_ref;

    Prints:

    1, label1 2, label2 3, label3 4, label4 5, label5

    Perl is environmentally friendly - it saves trees
Re: Sort AoH by another array
by johngg (Canon) on Nov 06, 2007 at 11:38 UTC
    Rather than using a lookup hash, you could build a sort order hash and use a Schwartzian Transform to sort into the new array. I have changed the desired order to illustrate the %sortOrder hash more clearly.

    use strict; use warnings; use Data::Dumper; my $raUserOrder = [ 5, 1, 4, 3, 2 ]; my %sortOrder = do { my $count = 0; map { $_ => ++ $count } @$raUserOrder; }; my $raQueryResults = [ { key => 4, name => q{label4} }, { key => 2, name => q{label2} }, { key => 5, name => q{label5} }, { key => 1, name => q{label1} }, { key => 3, name => q{label3} }, ]; my $raSortedQueries = [ map { $_->[0] } sort { $sortOrder{$a->[1]} <=> $sortOrder{$b->[1]} } map { [ $_, $_->{key} ] } @$raQueryResults ]; print Data::Dumper->Dumpxs( [ \%sortOrder, $raSortedQueries ], [ qw{ *sortOrder raSortedQueries } ] );

    Here's the output.

    %sortOrder = ( '4' => 3, '1' => 2, '3' => 4, '2' => 5, '5' => 1 ); $raSortedQueries = [ { 'name' => 'label5', 'key' => 5 }, { 'name' => 'label1', 'key' => 1 }, { 'name' => 'label4', 'key' => 4 }, { 'name' => 'label3', 'key' => 3 }, { 'name' => 'label2', 'key' => 2 } ];

    I hope this is of interest.

    Cheers,

    JohnGG

    Update: Corrected typo.

      Thanks, I tried the lookup table and also this and both seemed to work fine. I appreciate your help.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://649121]
Approved by GrandFather
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (8)
As of 2024-04-25 11:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found