Discussion:
[Templates] sorting a hash by values
Octavian Rasnita
2005-06-18 12:06:12 UTC
Permalink
Hi,

I have a hash reference like:

$ref = {
id1 => {
title => 'first title',
number => 123,
},
id2 => {
title => 'the second title',
number => 234,
},
id3 => {
title => 'the third title',
number => 345,
},
};

And I want to print the values in a template, sorting the titles by
"number", in html links, something like:

$vars = {
ref => $ref,
};

$tt->process("file.html", $vars);

The file file.html:

[% FOREACH r = ref.keys %]
<a href="/program?id=[% r %]">[% ref.title _ " " _ ref.number %]</a>
[% END %]

But I want those links be sorted by the ref.number. Is it possible? Or I
need to create another array with the keys of the hash I want to sort in
order and then loop that array?

Thank you.

Teddy
Josh Rosenbaum
2005-06-18 18:37:25 UTC
Permalink
I have a few suggestions for you.
1) Change your hash to:
$ref = {
123 => {
title => 'first title',
id => 'id1',
},
234 => {
title => 'the second title',
id => 'id2',
},
345 => {
title => 'the third title',
id => 'id3',
},
};

Then just do ref.keys.nsort in your FOREACH loop
[% FOREACH r = ref.keys.nsort %]
<a href="/program?id=[% ref.$r.id %]">[% ref.$r.title _ " " _ r %]</a>
[% END %]



2) Or if you can't change your hash.

Change this:
$vars = {
ref => $ref,
};

to:
$vars = {
ref => $ref,
sort_keys_by => sub{
my $hash = shift;
my $sort_field = shift;
return sort {
$hash->{$a}->{$sort_field} <=> $hash->{$b}->{$sort_field}
} keys %$hash
}
};

Then change your foreach to be:
[% FOREACH r = sort_keys_by(ref, 'number') %]
<a href="/program?id=[% r %]">[% ref.$r.title _ " " _ ref.$r.number %]</a>
[% END %]


3) Similar to 2. Add a virtual method to your hashes. Just add this above your process call. (Note: You might need to add: 'use Template::Stash;') :
$Template::Stash::HASH_OPS->{ sort_keys_by } = sub {
my ($hash, $sort_field) = @_;
return sort {
$hash->{$a}->{$sort_field} <=> $hash->{$b}->{$sort_field}
} keys %$hash
};

then you can do:
[% FOREACH r = ref.sort_keys_by('number') %]
<a href="/program?id=[% r %]">[% ref.$r.title _ " " _ ref.$r.number %]</a>
[% END %]


Sorry, if there are errors in this, I wrote it up pretty quick. I think you should be able to do what you want with it, though.

-- Josh
Post by Octavian Rasnita
Hi,
$ref = {
id1 => {
title => 'first title',
number => 123,
},
id2 => {
title => 'the second title',
number => 234,
},
id3 => {
title => 'the third title',
number => 345,
},
};
And I want to print the values in a template, sorting the titles by
$vars = {
ref => $ref,
};
$tt->process("file.html", $vars);
[% FOREACH r = ref.keys %]
<a href="/program?id=[% r %]">[% ref.title _ " " _ ref.number %]</a>
[% END %]
But I want those links be sorted by the ref.number. Is it possible? Or I
need to create another array with the keys of the hash I want to sort in
order and then loop that array?
Thank you.
Teddy
_______________________________________________
templates mailing list
http://lists.template-toolkit.org/mailman/listinfo/templates
!DSPAM:42b40fa7148758171910352!
Buddy Burden
2005-06-20 02:53:52 UTC
Permalink
Octavian,
Post by Octavian Rasnita
[% FOREACH r = ref.keys %]
<a href="/program?id=[% r %]">[% ref.title _ " " _ ref.number %]</a>
[% END %]
But I want those links be sorted by the ref.number. Is it possible? Or I
need to create another array with the keys of the hash I want to sort in
order and then loop that array?
Doesn't this

[% FOREACH r = ref.sort('number') %]

work?


-- Buddy
Josh Rosenbaum
2005-06-20 08:08:20 UTC
Permalink
This would not work since that usage of 'sort' is only for an array ref, and the original ref was a hash ref.

-- Josh
Post by Buddy Burden
Octavian,
Post by Octavian Rasnita
[% FOREACH r = ref.keys %]
<a href="/program?id=[% r %]">[% ref.title _ " " _ ref.number %]</a>
[% END %]
But I want those links be sorted by the ref.number. Is it possible? Or I
need to create another array with the keys of the hash I want to sort in
order and then loop that array?
Doesn't this
[% FOREACH r = ref.sort('number') %]
work?
-- Buddy
_______________________________________________
templates mailing list
http://lists.template-toolkit.org/mailman/listinfo/templates
!DSPAM:42b6306847537690717754!
Dave Cross
2005-06-20 09:06:17 UTC
Permalink
--zYM0uCDKw75PZbzx
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

[ reordering to make sense of the discussion ]
=20
Post by Buddy Burden
Doesn't this
[% FOREACH r =3D ref.sort('number') %]
work?
This would not work since that usage of 'sort' is only for an array ref,=
=20
and the original ref was a hash ref.
Actually, the "sort" vmethod does work on hash refs as well as array refs.
It returns a list of hash keys that you can use to process the hash in the
sorted order.

The suggested code doesn't work because the original poster wanted to
sort by a value hidden two values deep in a hash of hashes.

Dave...

--=20
Ain't there one damn song
That can make me break down and cry

--zYM0uCDKw75PZbzx
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQFCtocJhlYrNpFq+8ERAuYbAJ9RDPxCD7aly3rKyrjbBwS/CtXbDQCfUquu
m8pi2on3IpBD0vaNngziSoQ=
=/zj4
-----END PGP SIGNATURE-----

--zYM0uCDKw75PZbzx--
Josh Rosenbaum
2005-06-20 16:43:28 UTC
Permalink
Post by Dave Cross
[ reordering to make sense of the discussion ]
Post by Josh Rosenbaum
Post by Buddy Burden
Doesn't this
[% FOREACH r = ref.sort('number') %]
work?
This would not work since that usage of 'sort' is only for an array ref,
and the original ref was a hash ref.
Actually, the "sort" vmethod does work on hash refs as well as array refs.
It returns a list of hash keys that you can use to process the hash in the
sorted order.
The suggested code doesn't work because the original poster wanted to
sort by a value hidden two values deep in a hash of hashes.
Dave...
That's what I said. You'll notice the phrase "that usage of 'sort'" in my previous post.

-- Josh
Buddy Burden
2005-06-20 17:58:28 UTC
Permalink
Josh,
Post by Josh Rosenbaum
This would not work since that usage of 'sort' is only for an array ref,
and the original ref was a hash ref.
Ah, so it was.

I wonder if Octavian could recast his collection to be an array rather than a
hash then (similar to your first suggestion). It would depend, obviously, on
if he were ever referring to the elements by the "idN" tags or not.

Elsewise, were it me, I'd go with a new vmethod (which I believe was your 3rd
suggestion).


-- Buddy
Octavian Rasnita
2005-06-20 18:34:42 UTC
Permalink
Post by Buddy Burden
Josh,
Post by Josh Rosenbaum
This would not work since that usage of 'sort' is only for an array ref,
and the original ref was a hash ref.
Ah, so it was.
I wonder if Octavian could recast his collection to be an array rather than a
hash then (similar to your first suggestion). It would depend, obviously, on
if he were ever referring to the elements by the "idN" tags or not.
Elsewise, were it me, I'd go with a new vmethod (which I believe was your 3rd
suggestion).
No, unfortunately I cannot create an array ref because the hash contains a
list of articles identified by an MD5 hash, and each article has a title, a
sub-title, a rank (number), a body, the number of visits per that article,
and the number of comments, and a few other variables.

And I want to sort by the number of visits in a case, then by the number of
comments in other case, then by the rank, and so on, and I might need to
sort the hash by more of these fields, so I guess I will follow the
suggested ways.

Thank you.

Teddy

Continue reading on narkive:
Loading...