package Sort::LOH; use strict; use Carp; use vars qw($VERSION); $VERSION = '0.01'; ########################################################################### # D O C U M E N T A T I O N =head1 NAME Sort::LOH - Sorter for List of Hashes =head1 SYNOPSIS use strict; use Sort::LOH; my @SAMPLE_DATA = ( {F1 => "1", F2 => "2", F3 => "3", FLOAT => "2", ST => "123 Main Street"}, {F1 => "2", F2 => "3", F3 => "4", FLOAT => "9", ST => "45 Main Street",}, {F1 => "3", F2 => "4", F3 => "4", FLOAT => "045", ST => "2459 Main St"}, {F1 => "4", F2 => "5", F3 => "6", FLOAT => "1.3", ST => "2580 Main Street"}, {F1 => "5", F2 => "6", F3 => "7", FLOAT => "9", ST => "39 Main Street"}, {F1 => "6", F2 => "7", F3 => "8", FLOAT => "8.8", ST => "1888 Main Street"} ); my $sorter = Sort::LOH->new(\@LOH); my @sorted = $sorter->sortMe(["F3", "ST"]); Sorting in reverse order: my @sorted = $sorter->sortMe(["-F3", "-ST"]); Sorting numerically, as opposed to the default alphabetical: my @sorted = $sorter->sortMe(["FLOAT n"]); =head1 DESCRIPTION Takes in a LOH (List of Hashes) and an array of keys to sort by and returns a new, sorted LOH. This module closely relates to Sort::Fields in terms of it's interface how it does things. On of it's main differences is that it is OO, so one can create a Sort::LOH object and perform multiple sorts on it. =cut =head1 PUBLIC METHODS =cut # D O C U M E N T A T I O N ########################################################################### ########################################################################### # C O N S T R U C T O R =head2 new(\@LOH_to_sort) The class constructor. To create a Sort::LOH object, simply call: my $sorter = Sort::LOH->new(\@LOH); =cut sub new { my $class = shift; my $self = {}; bless $self, $class; unless (ref($self->{LOH} = shift) eq 'ARRAY') { croak 'LOH needs a reference to a List of Hashes'; } $self->{SORT_BY} = undef; return $self; } # C O N S T R U C T O R ########################################################################### ########################################################################### # S T A T I C M E T H O D S =head2 element_class() The name of the class for use in calling methods. This is a trick to simplify inheritance of static factory methods that I got from Perlmonks: http://www.perlmonks.org/index.pl?node_id=74924 Inheriting classes would create override element_class with the name of their class. =cut sub element_class { return "Sort::LOH"; } =head2 static(@sort_by, \@LOH_to_sort) A static method that allows caller to make the class do all the work with one swell foop: my @sorted = Sort::LOH->static(["F1", "F2"], \@SAMPLE_DATA); as opposed to my $sorter = Sort::LOH->new(\@LOH); my @sorted = $sorter->sortMe(["F1", "F2"]); If caller wants to do multiple sorts, one should use the constructor, and create a Sort::LOH object, since then one only has to pass in the data once: my $sorter = Sort::LOH->new(\@LOH); my @sorted = $sorter->sortMe(["F1", "F2"]); my @revSort = $sorter->sortMe(["-F1", "-F2"]); =cut sub static { my $self = shift; my @sortby = shift || croak 'USAGE: Sort::LOH->factory(@LIST, \@LOH)'; my @loh = shift || croak 'Sort::LOH::factory() needs 2 args'; my $sorter = $self->element_class()->new(@loh); return $sorter->sortMe(@sortby); } # S T A T I C M E T H O D S ########################################################################### ########################################################################### # C L A S S M E T H O D S =head2 sortMe(@sort_by) The workhorse of this class. Expects a list of the LOH keys to determine the sort order for the returned LOH. my @sorted = $sorter->sortMe(["F1", "F2"]); It is possible to do a reverse sort for a particular key by placing a minus sign at the front of it: my @sorted = $sorter->sortMe(["-F1", "-F2"]); If one wants to do a numeric sort, instead of a alphabetical sort, place " n" after the key in the list: my @sorted = $sorter->sortMe(["F1 n", "F2 n"]); =cut sub sortMe { my $self = shift; $self->{SORT_BY} = shift || croak 'LOH needs a list of fields to sort by'; my (@sortcode, @sortedLOH); for (@{$self->{SORT_BY}}) { unless (/^-?\w+\s*n?$/) { croak "improperly formatted sort column specifier '$_'"; } # Logic from Sort::Fields # Set a and b depending on '-' flag at the start of a key my ($a, $b) = /^-/ ? qw(b a) : qw(a b); # Is it a string or numeric sort? my $op = /\s+n$/ ? '<=>' : 'cmp'; # Get the actual column name my ($col) = /^-?(\w+)/; # Make sure that the sort key being passed in exists. if (exists($self->{LOH}[0]{$col})) { push @sortcode, "\$${a}->{${col}} $op \$${b}->{${col}}"; } } # Croak if there were no valid sort keys specified. unless ($sortcode[0]) { croak "No valid key match to sort LOH."; } my $sortcode = join " or ", @sortcode; $sortcode = "sort { $sortcode } \@{\$self->{LOH}};"; @sortedLOH = eval "$sortcode"; if ($@) { croak "Sort Failure of LOH\n$@"; } return @sortedLOH; } # C L A S S M E T H O D S ########################################################################### 1; __END__ =head1 BUGS =over =item * When a LOH is passed in that has a key that isn't present in each row in the list, and the class is sorted on that key, sort will print out errors for comparing with undefined values. For instance: my @the_data = ( {ID => "a", CITY => "f1 f"}, {ID => "b", CITY => "f2 a"}, {ID => "c"}, {ID => "f", CITY => "f6 e"} ); my $lohSorter = Sort::LOH->new(\@the_data); my @sorted = $lohSorter->sortMe(["CITY"]); Making sure that each key has an empty string as a defined value will solve this: my @better_data = ( {ID => "a", CITY => "f1 f"}, {ID => "b", CITY => "f2 a"}, {ID => "c", CITY => ""}, {ID => "f", CITY => "f6 e"} ); I haven't figured out a way to trap this error as of yet. =back =head1 SUPPORT The author always welcomes your comments, critiques, suggestions or requests. =head1 AUTHOR Christopher Baker http://www.ignatzmouse.com =head1 COPYRIGHT Copyright (c) 2002 Christopher Baker. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO Sort::File, Data::Table =cut