[comp.lang.perl] printing assoc array in non-alphabetical order

vince@bcsaic.UUCP (Vince Skahan) (05/04/91)

[...sigh...hope the previous Cancels on this took effect
	and you only got one...]

I have an associative array sorting question (I think)...
but it might be more related to keys.

The question is "how can I get an associative
	array to print in a specified (by me) order that 
	is NOT either alphabetic or reversed-alphabetic ?"

The following fragment prints:
	net.apr
	net.feb 
	net.jan 
	net.mar

when I want
	net.jan 
	net.feb 
	net.mar 
	net.apr

how can I get what I'm looking for in the following
example ???

--------------------- example -------------------
%files=("net.jan",1,"net.feb",2,"net.mar",3,"net.apr",4);
foreach $item (sort keys %files)
{
	print "$item\n";
}
-------------------------------------------------

let me give some more details if it helps...

1. I have a number of files, each with individual lines that
begin with the month-of-the-year (or do so after I get
through with splitting the 1st item per line), and
the data looks like this:

#monthname hostname bytes-in bytes-out errors char-per-sec
Jan        myhost     9000    3333      2       221
(etc...for many lines)

2. there are individual file names "net.monthname" for each
	month of the year that have to be reported on 
	(but not necessarily processed) in "month-of-year order"

3. I am taking a given month's data, and summarizing it based
on an associative array by processing it something like the 
follows:

	if $bytes{$monthname} isn't defined, init all the
		individual $somethings{$monthname} to 0

	fill in the various $somethings{$monthname} with
	something like:	 $bytes_in{$monthname} += $2;

	calculate all the $monthname specific totals, rates,
	etc. that I want to generate.

what I want to do is:

1. open the files if they exist (no sweat, I made a list 
	of files "net.*" and didn't die on failure to open)
2. do "good" stuff with them (I can already do this part
	for a given filename and generate the assoc. array)
3. print the per-month totals as follows:

	filename	category1	category2...

	net.jan		$total1{$jan}	$total2{$jan}	
	net.feb		$total1{$feb}	$total2{$feb}	
	net.mar		$total1{$mar}	$total2{$mar}	
	   .
	   .
	   .
	net.dec		$total1{$dec}	$total2{$dec}	

	totals		(sum-of-above)	(sum-of-above)


the other way of looking at it, is given an associative array
	with a number of items in it, how do I get it to print
	in the desired order (which is NOT alphabetical) ?

any help would be appreciated and I'll share the real code via e-mail
if it'll help...

					Thanks...
-- 
----------------------------------------------------------------
                         Vince Skahan   
 vince@atc.boeing.com                  ...uw-beaver!bcsaic!vince
 (place in heaven assured...I finished my 3-yr-old's swing set)

merlyn@iwarp.intel.com (Randal L. Schwartz) (05/09/91)

In article <46485@bcsaic.UUCP>, vince@bcsaic (Vince Skahan) writes:
| %files=("net.jan",1,"net.feb",2,"net.mar",3,"net.apr",4);
| foreach $item (sort keys %files)
| {
| 	print "$item\n";
| }

Gosh.  You're closer than you think, perhaps.  Try this:

%files=("net.jan",1,"net.feb",2,"net.mar",3,"net.apr",4);

sub by_files {
	$files{$a} <=> $files{$b}; # use the spaceship operator
}

foreach $item (sort by_files keys %files) {
	print "$item\n";
}

But couldn't you also just go:

foreach $item ('net.jan', 'net.feb', 'net.mar', 'net.apr') {
	print "$item, $total1{$item}, $total2{$item}\n"
}

You don't need to sort the keys if you don't want to!

print "Just another Perl hacker,"
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/

tchrist@convex.COM (Tom Christiansen) (05/09/91)

From the keyboard of vince@bcsaic.UUCP (Vince Skahan):
:[...sigh...hope the previous Cancels on this took effect
:	and you only got one...]
:
:I have an associative array sorting question (I think)...
:but it might be more related to keys.
:
:The question is "how can I get an associative
:	array to print in a specified (by me) order that 
:	is NOT either alphabetic or reversed-alphabetic ?"
:
:The following fragment prints:
:	net.apr
:	net.feb 
:	net.jan 
:	net.mar
:
:when I want
:	net.jan 
:	net.feb 
:	net.mar 
:	net.apr
:
:how can I get what I'm looking for in the following
:example ???
:
:--------------------- example -------------------
:%files=("net.jan",1,"net.feb",2,"net.mar",3,"net.apr",4);
:foreach $item (sort keys %files)
:{
:	print "$item\n";
:}
:-------------------------------------------------

You need to declare a subroutine to do the sorting for
you.  This is similar to what you do with qsort(3), if
you're familiar with that.

In this case, it's pretty easy, because you already
have an array whose values are the sorting order.

Given your example, all you need to do is this:

    for $item (sort byval keys %files) {
	print "$item\n";
    }
    sub byval { $files{$a} <=> $files{$b}; }

Somewhat more generally, you may want an array like this:

    @month{'jan', 'feb', 'mar', 'apr',
	   'may', 'jun', 'jul', 'aug',
	   'sep', 'oct', 'nov', 'dec'} = 1..12;
    
and a sort routine like this:

    sub bymonth {
	local($m1) = $a =~ /\.\w{3}/;
	local($m2) = $b =~ /\.\w{3}/;
	$month{$m1} <=> $month{$m2};
    }


For large data sets you wouldn't really want to do the regexp each time.

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
		"So much mail, so little time."