Re: Sorting - lower to upper
by Joost (Canon) on Jul 15, 2004 at 15:45 UTC
|
A hash is inherently unsorted. You can sort a list of keys and store them in an array, though:
my @keys = sort keys %hash; # adapt to your needs here.
for (@keys) {
print "$_ => $hash{$_}\n";
}
edit: %hash{$_} -> $hash{$_}
edit2: This seems to me reasonably efficient:
my @keys = map { tr/a-zA-Z/A-Za-z/; $_ } sort map { tr/a-zA-Z/A-Za-z/;
+ $_ } keys %hash;
| [reply] [d/l] [select] |
|
... map { (my $x = $_) =~ tr/a-zA-Z/A-Za-z/; $x } ...
which ensures that I get the desired output value without
seriously mangling my input value as a side-effect.
| [reply] [d/l] [select] |
Re: Sorting - lower to upper
by pelagic (Priest) on Jul 15, 2004 at 15:51 UTC
|
use strict;
use Data::Dumper;
my %h = (
'John' => 'P9111',
'Bob' => 4711,
'xavier' => '20040610',
'alice' => '20040610',
);
my %sk = map ({$_, ($_ =~ m/^[a-z]/) ? 0 : 1} (keys %h));
print Dumper %sk;
my @xk = (sort {$sk{$a} <=> $sk{$b} || $a cmp $b} keys %sk);
print Dumper @xk;
___OUTPUT___
$VAR1 = 'Bob';
$VAR2 = 1;
$VAR3 = 'alice';
$VAR4 = 0;
$VAR5 = 'John';
$VAR6 = 1;
$VAR7 = 'xavier';
$VAR8 = 0;
$VAR1 = 'alice';
$VAR2 = 'xavier';
$VAR3 = 'Bob';
$VAR4 = 'John';
| [reply] [d/l] |
|
print Dumper %sk;
print Dumper @xk;
You might consider using it like so:
print Dumper \%sk;
print Dumper \@xk;
You should find the output a bit more sensible. | [reply] [d/l] [select] |
|
Yeah, thanks, that was actually a typo. I always use the reference with Dumper.
| [reply] |
Re: Sorting - lower to upper
by hardburn (Abbot) on Jul 15, 2004 at 15:42 UTC
|
with the following keys, in the following order
No, you don't. Hashes don't have any defined order. If you want order, use an array.
----
send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.
| [reply] |
Re: Sorting - lower to upper
by shemp (Deacon) on Jul 15, 2004 at 15:54 UTC
|
Heres a not very tight implementation, but it should make clear whats going on:
{
my %hash = (
'Ask' => 1,
'Bob' => 1,
'Cat' => 1,
'amy' => 1,
'ben' => 1,
'can' => 1,
);
print join "\n", sort case_sorter keys %hash;
}
sub case_sorter {
my $first_a = substr $a, 0, 1;
my $first_b = substr $b, 0, 1;
if ( ($first_a eq uc($first_a)) && ($first_b eq uc($first_b)) ) {
return $a cmp $b;
}
elsif ( $first_a eq uc($first_a) ) {
return 1;
}
elsif ( $first_b eq uc($first_b) ) {
return -1;
}
else {
return $a cmp $b;
}
}
| [reply] [d/l] |
Re: Sorting - lower to upper
by ccn (Vicar) on Jul 15, 2004 at 15:54 UTC
|
You can not have a sorted hash, but you can sort it's keys before use
foreach ( sort { ord($a) <=> ord($b) } keys %hash ) {
print "$_ => $hash{$_}\n"
}
| [reply] [d/l] |
|
Your comparison is backwards, the OP wants the lowercase first so you need:
foreach ( sort { ord($b) <=> ord($a) } keys %hash ) {
print "$_ => $hash{$_}\n"
}
Kudos though, much more elegant than the solution i posted! | [reply] [d/l] |
|
That's not quite what was asked by the OP either.
output is:
xavier => 1
alice => 1
John => 1
Bob => 1
| [reply] [d/l] |
|
| [reply] |
Re: Sorting - lower to upper
by shemp (Deacon) on Jul 15, 2004 at 16:44 UTC
|
This question has really intrigued me, so sorry for the flurry of posts. I was thinking about extending the sort concept to include the same case concerns for all characters in the strings.
The original question would lead to odd results when comparing 'Amy' to 'AMy', because 'AMy' would come before 'Amy', since 'M' comes before 'm'. So i was thinking, if lowercase comes before upper case on the first letter, why not for subsequent letters, so heres a neat sorter to deal with it:
(my $rev_a = $a) =~ tr/A-Za-z/a-zA-Z/;
(my $rev_b = $b) =~ tr/A-Za-z/a-zA-Z/;
return $rev_a cmp $rev_b;
It just exploits that we want normal string cmp(), except that same letter different case sorting is reversed. | [reply] [d/l] |
Re: Sorting - lower to upper
by mifflin (Curate) on Jul 15, 2004 at 15:58 UTC
|
erickn@isfe:/home/erickn> cat t
use warnings;
$hash{John} = '';
$hash{Bob} = '';
$hash{xavier} = '';
$hash{alice} = '';
for my $key (sort {ucfirst($b) cmp lcfirst($a)} keys %hash) {
print "$key\n";
}
erickn@isfe:/home/erickn> perl t
alice
xavier
Bob
John
| [reply] [d/l] |
|
Nope, wont work. Just consider $a = 'amy', $b = 'Ask'. after the case conversion, its comparing then as they were originally, and the capital comes before the lower case.
| [reply] |
Re: Sorting - lower to upper
by Roy Johnson (Monsignor) on Jul 15, 2004 at 17:27 UTC
|
@keys = ((sort grep(/^[a-z]/, keys %hash)), (sort grep(/^[A-Z]/, keys
+%hash)));
We're not really tightening our belts, it just feels that way because we're getting fatter.
| [reply] [d/l] |
|
That'll make duplicate copies of everything that doesn't begin with a letter. Maybe you want to narrow those regexen down a bit.
update:
Oops, confused [^a-z] with ^[a-z]. So yes, everything without a letter will be absent, not duplicated. Doesn't change that there's something wrong with this solution.
| [reply] [d/l] [select] |
|
| [reply] |
|
It seems to me that rather than duplicate all non-first-lettered-words (nice hyphenate don't you think?), it actually discards all these words. I mean, the first list sorts all words which start with a small-case and the second list sorts all the first-upper-cased-words.
I would appreciate explanation.
Update: It seems that my pertinent and correct remark was voted down at least two times. I wonder what that means.
| [reply] |
|
Re: Sorting - lower to upper
by orderthruchaos (Scribe) on Jul 15, 2004 at 17:02 UTC
|
my %hash = qw/John 1 Bob 1 xavier 1 alice 1/;
my @keys = map { tr/a-zA-Z/A-Za-z/; $_ }
sort
map { tr/a-zA-Z/A-Za-z/; $_ } keys %hash;
print "@keys\n";
Will give you
alice xavier Bob John
| [reply] [d/l] [select] |
|
| [reply] |
|
Taking merlyn's advice into account, I'll change my answer;-)
--from above--
Don't forget about the Schwartzian Transform:
my %hash = qw/John 1 Bob 1 xavier 1 alice 1/;
my @keys = map { (my $x = $_) =~ tr/a-zA-Z/A-Za-z/; $x }
sort
map { (my $x = $_) =~ tr/a-zA-Z/A-Za-z/; $x }
keys %hash;
print "@keys\n";
Will give you
alice xavier Bob John
| [reply] [d/l] [select] |
|
Sorry about that... this thread is growing too fast for me to keep up!
| [reply] |