CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
Can SPLICE be used to sort an array of hash references?

 



PapaGeek
User

Feb 22, 2014, 8:54 AM

Post #1 of 8 (1444 views)
Can SPLICE be used to sort an array of hash references? Can't Post

Can SPLICE be used to sort an array of hash references?

I need to find a way to sort an array of hash references based on the content of one of the hashed items. I know how to do this with a bubble sort, but that is in no way the best answer. If SPLICE can be used to change for example (1 2 3 5 6 4 7 9 8) into (1 2 3 4 5 6 7 9 8) then (1 2 3 4 5 6 7 8 9) by calling SPLICE twice, once each time a non-ordered value is found, Im sure that would be more efficient.

What is the proper way of doing this?

Here is an example of the data Im trying to create:


Code
3m  6m  1Yr 3Yr 5Yr Ticker 3m 6m 1Y 3Y 5Y Rank  
15% 19% 48% 25% 25% PPCIX 2 2 1 1 2 8
13% 17% 44% 23% 30% FSCRX 3 3 2 2 1 11
15% 19% 43% 20% 22% PSSIX 1 1 3 3 4 12
11% 13% 37% 16% 22% OAKIX 6 5 5 9 6 31
9% 13% 39% 18% 22% POGRX 12 7 4 4 7 34
11% 13% 32% 17% 21% NSEIX 8 6 7 6 8 35
11% 12% 30% 18% 18% FUSVX 5 9 9 5 9 37
10% 12% 31% 16% 23% NMCIX 10 8 8 8 3 37
10% 11% 32% 17% 22% MPSIX 9 10 6 7 5 37
13% 15% 26% 9% 16% ICEIX 4 4 10 12 12 42
11% 8% 16% 9% 17% HLMIX 7 11 13 13 10 54
8% 7% 23% 15% 16% BEGIX 13 13 11 10 11 58
9% 8% 21% 14% 15% RLBGX 11 12 12 11 13 59
5% 4% 9% 8% 11% MACIX 14 14 14 14 14 70
1% -1% 0% 4% 8% SDGIX 16 19 15 16 16 82
3% -1% -1% 4% 8% PTTRX 15 18 19 15 15 82
1% 0% 0% 1% 3% VSGDX 18 15 16 18 18 85
0% 0% 0% 0% 0% AKGXX 19 16 17 19 20 91
1% -4% -8% 3% 7% VIPSX 17 20 20 17 17 91
0% 0% 0% 0% 0% FRTXX 20 17 18 20 19 94


The last 6 columns of each row will be zero when the data first comes in, and the rows will basically be in random order. The array has to be sorted first by the 3m percentage column and then the ranking 3m column is filled in as shown. In this case, 1 has the highest return for 3 months and 20 has the lowest. The same process is done for each of the other 4 percentage columns and then all of the rankings are added together to create an overall Rank and the array is finally sorted by that overall Rank.


Laurent_R
Veteran / Moderator

Feb 22, 2014, 11:06 AM

Post #2 of 8 (1434 views)
Re: [PapaGeek] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

Don't use bubble sort for that. And don't use splice either. Use the sort function of Perl, which will be far more efficient in terms of sorting speed and will take care of all the gory details.

A quick example. First a hash:


Code
my %months = (jan => 1, feb => 2, mar => 3, apr = 4, may => 5);


Dumping the content gives me an unordered list (since hashes have no usable order):


Code
0  HASH(0x80359de8) 
'apr' => 4
'feb' => 2
'jan' => 1
'mar' => 3
'may' => 5


Now printing the sorted output (hash keys printed in accordance to order of the hash values):


Code
print join "$_\n", sort {$months{$a} <=> $months{$b} } keys %months


This prints:


Code
jan 
feb
mar
apr
may


If you have a hash ref instead of a hash just change $months{$a} to whatever suits your data structure, for example $months->{$a}, and similarly for $months{$b}. If you have trouble with this, please show your data structure, we will be able to tell you how to sort it.


(This post was edited by Laurent_R on Feb 22, 2014, 11:08 AM)


Chris Charley
User

Feb 22, 2014, 6:25 PM

Post #3 of 8 (1406 views)
Re: [PapaGeek] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

I think this is a fairly complex problem. I used your sample data to come up with a solution. It uses a hash of hashes and a hash of arrays and also introduces something called a hash slice. A hash slice is used when you want to assign to more than one hash element at a time or retrieve more than one hash element at a time. The docs explain some simple hash and array slices http://perldoc.perl.org/perldata.html#Slices]. Click on the menu for 'slices' and it will explain them.

The output I got was:

Code
3m  6m  1Yr 3Yr 5Yr Ticker 3m 6m 1Yr 3Yr 5Yr Rank 
15% 19% 48% 25% 25% PPCIX 1 1 1 1 2 6
13% 17% 44% 23% 30% FSCRX 2 2 2 2 1 9
15% 19% 43% 20% 22% PSSIX 1 1 3 3 4 12
9% 13% 39% 18% 22% POGRX 5 4 4 4 4 21
11% 13% 37% 16% 22% OAKIX 3 4 5 6 4 22
11% 13% 32% 17% 21% NSEIX 3 4 6 5 5 23
10% 11% 32% 17% 22% MPSIX 4 6 6 5 4 25
10% 12% 31% 16% 23% NMCIX 4 5 7 6 3 25
11% 12% 30% 18% 18% FUSVX 3 5 8 4 6 26
13% 15% 26% 9% 16% ICEIX 2 3 9 9 8 31
11% 8% 16% 9% 17% HLMIX 3 7 12 9 7 38
8% 7% 23% 15% 16% BEGIX 6 8 10 7 8 39
9% 8% 21% 14% 15% RLBGX 5 7 11 8 9 40
5% 4% 9% 8% 11% MACIX 7 9 13 10 10 49
1% -1% 0% 4% 8% SDGIX 9 11 14 11 11 56
3% -1% -1% 4% 8% PTTRX 8 11 15 11 11 56
1% 0% 0% 1% 3% VSGDX 9 10 14 13 13 59
1% -4% -8% 3% 7% VIPSX 9 12 16 12 12 61
0% 0% 0% 0% 0% AKGXX 10 10 14 14 14 62
0% 0% 0% 0% 0% FRTXX 10 10 14 14 14 62

The attached file is the program, (t9.pl).
Attachments: t9.pl (2.69 KB)


PapaGeek
User

Feb 22, 2014, 6:27 PM

Post #4 of 8 (1405 views)
Re: [Laurent_R] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

Im not sure that I am following you on this one. It looks like you are sorting the hash itself, not an array of hash references.

Here is a program that will create the kind of data that I will be working with.


Code
use Modern::Perl '2013'; 

my %dataHash;
my @hashArray;

insertHash(15, 19, 48, 25, 25, "PPCIX");
insertHash(11, 13, 32, 17, 21, "NSEIX");
insertHash(13, 17, 44, 23, 30, "FSCRX");
insertHash(10, 14, 39, 18, 22, "POGRX");
insertHash(14, 19, 43, 20, 26, "PSSIX");
insertHash(12, 15, 37, 16, 24, "OAKIX");

my $sizeOfArray = scalar (@hashArray);

print "The final array of hash references has $sizeOfArray items\n";
my $hashRef = $hashArray[0];

for (my $index = 0; $index < $sizeOfArray; $index++)
{
my $hashRef = $hashArray[$index];
foreach (sort keys %{$hashRef}) { print "$_=", ${$hashRef}{$_}, " "; }
print "\n";
}

sub insertHash {

my ($p3m,$p6m,$p1y,$p3y,$p5y,$ticker) = @_;

my %dataHash;

$dataHash{"p3m"} = $p3m;
$dataHash{"p6m"} = $p6m;
$dataHash{"p1y"} = $p1y;
$dataHash{"p3y"} = $p3y;
$dataHash{"p5y"} = $p5y;
$dataHash{"ticker"} = $ticker;
$dataHash{"r3m"} = 0;
$dataHash{"r6m"} = 0;
$dataHash{"r1y"} = 0;
$dataHash{"r3y"} = 0;
$dataHash{"r5y"} = 0;
$dataHash{"rank"} = 0;

push (@hashArray, \%dataHash);
}


This code produces a hash for each set of percentage returns and places a reference to each hash in an array. Here is what the array of hash references looks like at the end.


Code
The final array of hash references has 6 items 
p1y=48 p3m=15 p3y=25 p5y=25 p6m=19 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=PPCIX
p1y=32 p3m=11 p3y=17 p5y=21 p6m=13 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=NSEIX
p1y=44 p3m=13 p3y=23 p5y=30 p6m=17 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=FSCRX
p1y=39 p3m=10 p3y=18 p5y=22 p6m=14 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=POGRX
p1y=43 p3m=14 p3y=20 p5y=26 p6m=19 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=PSSIX
p1y=37 p3m=12 p3y=16 p5y=24 p6m=15 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=OAKIX


What needs to be done next is the sort the array by each of the p (Percentage) values and then define the corresponding r (Rank) value so that the highest percentage in each category has a corresponding rank of 1 and in this case of 6 rows, the lowest percentage has a rank of 6. After all the rows are sorted for each of the 5 percentages and the appropriate ranks are defined for each percentage time frame, all 5 of the ranks are added together to give the overall rank of the ticker symbol and the data can be displayed in the order of lowest to highest overall rank.

If these are the ticker symbols that you can invest your 401K money in, you want to be invest in the symbols at the top of the list, not the bottom.

So the question is, how do I sort the list of hash references based on the content of a particular column within each hash.

Id like to either find the build in sub or write a sub something like:
SortHashArray(@hashArray, p3m);


Kenosis
User

Feb 22, 2014, 9:19 PM

Post #5 of 8 (1389 views)
Re: [PapaGeek] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

Add the following sub:

Code
sub SortHashArray { 
my ( $arrRef, $col ) = @_;
exists $$arrRef[0]->{$col} or die "The key '$col' doesn't exist!\n";
@$arrRef = sort { $b->{$col} <=> $a->{$col} } @$arrRef;
}

And then call it right after the last insertHash():

Code
... 
insertHash(12, 15, 37, 16, 24, "OAKIX");

SortHashArray( \@hashArray, 'p3m' );


Note that the subroutine is sent a reference to the AoH that you want sorted, since it's better not (and unnecessary) to copy the entire AoH for the sort.

I'm sure you notice that the anonymous sub after sort dereferences the reference passed to it, and uses the value of $col as the key to the hash value.

Also placed a line to check whether the col (key) sent exists. If not, the script will die instead if throwing errors during each sort comparison.

Sorted on p3m output:

Code
The final array of hash references has 6 items 
p1y=48 p3m=15 p3y=25 p5y=25 p6m=19 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=PPCIX
p1y=43 p3m=14 p3y=20 p5y=26 p6m=19 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=PSSIX
p1y=44 p3m=13 p3y=23 p5y=30 p6m=17 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=FSCRX
p1y=37 p3m=12 p3y=16 p5y=24 p6m=15 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=OAKIX
p1y=32 p3m=11 p3y=17 p5y=21 p6m=13 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=NSEIX
p1y=39 p3m=10 p3y=18 p5y=22 p6m=14 r1y=0 r3m=0 r3y=0 r5y=0 r6m=0 rank=0 ticker=POGRX


Hope this helps!


(This post was edited by Kenosis on Feb 22, 2014, 9:35 PM)


PapaGeek
User

Feb 22, 2014, 9:47 PM

Post #6 of 8 (1376 views)
Re: [Kenosis] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

My special thanks to all. Here is the final code that solves the problem. I also added a feature where if the rank of multiple hashes will be the same when the percentage value remains the same.

Code
use Modern::Perl '2013'; 

my %dataHash;
my @hashArray;

print "Build the random array with all ranks = 0\n";
insertHash(15, 19, 48, 25, 25, "PPCIX");
insertHash(11, 13, 32, 17, 21, "NSEIX");
insertHash(13, 17, 44, 23, 30, "FSCRX");
insertHash(10, 14, 39, 18, 22, "POGRX");
insertHash(14, 19, 43, 20, 26, "PSSIX");
insertHash(13, 15, 37, 16, 24, "OAKIX");

print "Sort and rank array on each percentage return\n";
sortAndRank("p3m","r3m");
sortAndRank("p6m","r6m");
sortAndRank("p1y","r1y");
sortAndRank("p3y","r3y");
sortAndRank("p5y","r5y");

print "Create overall rank for each ticker symbol\n";
for (my $index = 0; $index < scalar (@hashArray); $index++)
{
my $hashRef = $hashArray[$index];
${$hashRef}{rank} = ${$hashRef}{r3m} + ${$hashRef}{r6m} +
${$hashRef}{r1y} + ${$hashRef}{r3y} + ${$hashRef}{r5y} ;
}

print "Print results sorted by overall rank\n";
my @sorted = sort { $a->{rank} <=> $b->{rank} } @hashArray;

for (my $index = 0; $index < scalar (@sorted); $index++)
{
my $hashRef = $sorted[$index];
foreach (sort keys %{$hashRef}) { print "$_=", ${$hashRef}{$_}, " "; }
print "\n";
}

sub sortAndRank
{
my ( $sortOn, $rank) = @_;

my @sorted = sort { $b->{$sortOn} <=> $a->{$sortOn} } @hashArray;

my $sizeOfArray = scalar (@hashArray);
if ($sizeOfArray == 0) { return; }

my $hashRef = $sorted[0];
${$hashRef}{$rank} = 1;
my $lastVal = ${$hashRef}{$sortOn};
my $lastRank = 1;

for (my $index = 0; $index < $sizeOfArray; $index++)
{
my $hashRef = $sorted[$index];
if ($lastVal != ${$hashRef}{$sortOn})
{
$lastRank = $index+1;
$lastVal = ${$hashRef}{$sortOn};
}
${$hashRef}{$rank} = $lastRank;
}
}

sub insertHash {

my ($p3m,$p6m,$p1y,$p3y,$p5y,$ticker) = @_;

my %dataHash;

$dataHash{"p3m"} = $p3m;
$dataHash{"p6m"} = $p6m;
$dataHash{"p1y"} = $p1y;
$dataHash{"p3y"} = $p3y;
$dataHash{"p5y"} = $p5y;
$dataHash{"ticker"} = $ticker;
$dataHash{"r3m"} = 0;
$dataHash{"r6m"} = 0;
$dataHash{"r1y"} = 0;
$dataHash{"r3y"} = 0;
$dataHash{"r5y"} = 0;
$dataHash{"rank"} = 0;

push (@hashArray, \%dataHash);
}

and here is the output the code produces:

Code
Build the random array with all ranks = 0 
Sort and rank array on each percentage return
Create overall rank for each ticker symbol
Print results sorted by overall rank
p1y=48 p3m=15 p3y=25 p5y=25 p6m=19 r1y=1 r3m=1 r3y=1 r5y=3 r6m=1 rank=7 ticker=PPCIX
p1y=44 p3m=13 p3y=23 p5y=30 p6m=17 r1y=2 r3m=3 r3y=2 r5y=1 r6m=3 rank=11 ticker=FSCRX
p1y=43 p3m=14 p3y=20 p5y=26 p6m=19 r1y=3 r3m=2 r3y=3 r5y=2 r6m=1 rank=11 ticker=PSSIX
p1y=37 p3m=13 p3y=16 p5y=24 p6m=15 r1y=5 r3m=3 r3y=6 r5y=4 r6m=4 rank=22 ticker=OAKIX
p1y=39 p3m=10 p3y=18 p5y=22 p6m=14 r1y=4 r3m=6 r3y=4 r5y=5 r6m=5 rank=24 ticker=POGRX
p1y=32 p3m=11 p3y=17 p5y=21 p6m=13 r1y=6 r3m=5 r3y=5 r5y=6 r6m=6 rank=28 ticker=NSEIX

Again, thanks to all for your help with this coding question.


(This post was edited by PapaGeek on Feb 22, 2014, 9:49 PM)


Laurent_R
Veteran / Moderator

Feb 23, 2014, 2:52 AM

Post #7 of 8 (1361 views)
Re: [PapaGeek] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post


In Reply To
Im not sure that I am following you on this one. It looks like you are sorting the hash itself, not an array of hash references.


Yes, I said it was a simple hash, the aim was only to demonstrate the use of the sort function. As I said, the syntax would have to be adapted to your data structure, but I could not really give a detailed solution not knowing what your data structure looked like. Now that you have provided information on the data structure, it is possible to give the exact syntax to sort your data, and Kenosis has supplied the solution to do it.


PapaGeek
User

Feb 23, 2014, 5:02 AM

Post #8 of 8 (1356 views)
Re: [Laurent_R] Can SPLICE be used to sort an array of hash references? [In reply to] Can't Post

Thank you Laurent,

This is my first real Perl project, I skipped Hello, World! Here is a brief description of what Im working on!

Im trying to automate the monthly process that I follow to manage my 401K / IRA accounts. Im taking on a lot of new Perl things, but Ive got over 45 years of programming experience! Im using Win32::GUI as my front end, not sure yet what the database will be, and the result will be a few visible GUI pages and a full Intranet website.

Ive create a DBaccess package to isolate the actual database from the rest of the program. Right now Im working on a set of real calls to the package that return fake data so I can test the GUI, program logic, and screen scrape software.

The GUI will let you manage lists of accounts, ticker symbols, and current investment amounts. There will be a relation as to what account can invest in what and how much is invested in that account.

The monthly process is to screen scrape the net to update the historical prices on each ticker and calculate the percentage returns over various time frames, 1 week, 3 months, 5 years, etc. After update all of the ticker returns, account by account I read in the ticker data that each account can invest in, create the overall rank as described in this post, and display the information ordered from best return to worse return. The display will also include things like market segment (mid-cap, small-cap, etc) and the amount I currently have invest in that ticker for that account.

The idea is to look at my investment monthly, try to keep the investment in the tickers at or near the top of the list while using the latest week and month returns along with the market segment to help in the decision of what investment if any should be changed. The process does not make any investment decisions, it merely shows you where your money currently is.

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives