Sorting a reference to an array of rows where each row is stored as a hash

80 views Asked by At

I'm trying to sort the following data structure in Perl, by location_id.

my $employees = $dbh->selectall_arrayref(qq[
    SELECT name, type, code, emp_cat_id,
           percentage, location_id
    FROM table_1
],{ Slice => {} });
 
for my $row (@$employees) {
   push @{
      $args->{employees}{ $row->{emp_cat_id} }
   }, $row;
}

Example:

123 => [
   {
      percentage  => 0.25,
      code        => "XYZ",
      name        => "John Doe",
      type        => "pt",
      location_id => 001,
      emp_cat_id  => 123

   }
],
555 => [
   {
      percentage  => 0.50,
      code        => "ZZZ"
      name        => "Chris Cringle",
      type        => "ft",
      location_id => 007,
      emp_cat_id  => 555

   },
   {
      percentage  => 0.25,
      code        => "XXX"
      name        => "Tom Thompson",
      type        => "pt",
      location_id => 002,
      emp_cat_id  => 555

   }
]

For every emp_cat_id, I need the structure to have the location_ids in asc order.

I've tried the following, but I get "useless use of sort in void context at line #" or "useless use of sort in scalar context at line #" errors.

$args->{employees} = sort {
   $a->{location_id} <=> $b->{location_id}
} $args->{employees};

Any help understanding the sort is appreciated!

2

There are 2 answers

0
Shawn On

So, you have a hashref where each element is an arrayref of hashrefs that should be sorted based on a key of that inside hashref?

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

my $hashref = {
    123 => [
        {
            percentage  => 0.25,
            code        => "XYZ",
            name        => "John Doe",
            type        => "pt",
            location_id => 001,
            emp_cat_id  => 123
        }
        ],
    555 => [
        {
            percentage  => 0.50,
            code        => "ZZZ",
            name        => "Chris Cringle",
            type        => "ft",
            location_id => 007,
            emp_cat_id  => 555
        },
        {
            percentage  => 0.25,
            code        => "XXX",
            name        => "Tom Thompson",
            type        => "pt",
            location_id => 002,
            emp_cat_id  => 555
        }
        ]
};

foreach my $arrayref (values %$hashref) {
    @$arrayref = sort { $a->{location_id} <=> $b->{location_id} } @$arrayref;
}

print Dumper($hashref);

The important part you're missing is in dereferencing the arrayrefs. @$arrayref instead of just $arrayref.

0
zdim On

The problem is that you are sorting the array(ref) at emp_cat_id of 555, then of 123, and so need to dereference for sorting those arrayrefs. So

foreach my $id (keys $args->{employees}) { 
    @{ $args->{employees}{$id} } = sort { 
            $a->{location_id} <=> $b->{location_id} 
        }
        @{ $args->{employees}{$id} } 
}

(tested with the structure shown in the question, omitted here)

Doing anything like this loses 007 into 7. This is of course possible to fix, let me know if it matters.

If you really have only the key employees then consider extracting the $args->{employees} hashref and working with that. It'll be way easier

use Storable qw(dclone);

my $employees = dclone $args->{employees};  # need deep copy

Oh well, here's the whole thing

use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);

my $args = {
    employees => {
        123 => [
            {
                percentage  => 0.25,
                code        => "XYZ",
                name        => "John Doe",
                type        => "pt",
                location_id => 001,
                emp_cat_id  => 123
            }
        ],
        555 => [
            {
                percentage  => 0.50,
                code        => "ZZZ",
                name        => "Chris Cringle",
                type        => "ft",
                location_id => 007,
                emp_cat_id  => 555

            },
            {
                percentage  => 0.25,
                code        => "XXX",
                name        => "Tom Thompson",
                type        => "pt",
                location_id => 002,
                emp_cat_id  => 555

            }
        ]
    }
};

foreach my $id (keys $args->{employees}) {
    @{ $args->{employees}{$id} } = sort {
            $a->{location_id} <=> $b->{location_id}
        }
        @{ $args->{employees}{$id} }
}

dd $args;