How does the sort_by function work?
Based on this post, I am trying to understand how sort_by
works in JSON::PP
.
When I run this code
#!/usr/bin/perl
use strict;
use warnings;
use JSON::PP;
use Data::Dumper qw(Dumper);
my $h = {
22 => { title => "c", name => "d" },
1 => { title => "1", name => "a" },
10 => { title => "a", name => "c" },
5 => { title => "b", name => "b" },
};
my $sorter = sub {
# See what's going on.
print "$JSON::PP::a cmp $JSON::PP::b\n";
print Dumper(\@_, $_);
<STDIN>; # press return to continue
$JSON::PP::a cmp $JSON::PP::b
};
my $js = JSON::PP->new;
my $output = $js->sort_by($sorter)->encode($h);
print $output . "\n";
it first sorts the inner keys, and then the outer keys, which determines the final order in the JSON string.
Right now it outputs
{"1":{"name":"a","title":"1"},"10":{"name":"c","title":"a"},"22":{"name":"d","title":"c"},"5":{"name":"b","title":"b"}}
and what I would like to end up with is that it is sorted by title
ie.
{"1":{"name":"a","title":"1"},"5":{"name":"b","title":"b"}"10",{"name":"c","title":"a"},"22":{"name":"d","title":"c"}}
I suppose the first problem is to disable the last outter key sort?
Then how do I get hold of th开发者_如何学编程e value of title
? When the algorithm runs, $JSON::PP::a
and $JSON::PP::b
contains the value name
and title
from the same hash.
This I can't figure out. Can anyone explain this, and/or help me write this algorithm?
You can't, or at least not easily. The function you give to sort_by
only has access to the keys being sorted. In order to do what you want you'd need to have access to the values associated with those keys (or, more likely, the hashref to which the keys belong, so you could look up the values yourself). That seems like it would be a useful enhancement; you might submit a feature request.
If your data structure is simple enough (which your example seems to be), you could keep a reference to the hash yourself. In order to do that, you have to be able to distinguish the keys in the inner hashes from the keys in the outer hashes (so you know which hash is being sorted, and thus what kind of comparison to do).
my $sorter = sub {
if ($JSON::PP::a =~ /^\d+$/) {
return $h->{$JSON::PP::a}{title} cmp $h->{$JSON::PP::b}{title};
}
return $JSON::PP::a cmp $JSON::PP::b
};
Try something like:
my $sorter = sub {
my $h = $_[0];
# simple check for if we are too deep
# just sort by keys in that case
return $JSON::PP::a cmp $JSON::PP::b
if ref($h->{$JSON::PP::a}) ne 'HASH';
# sort by titles value
return $h->{$JSON::PP::a}{title} cmp $h->{$JSON::PP::b}{title};
};
$_[0]
is the hash currently being sorted, altho this does not seem to be documented (so possibly unreliable).
It works for this case, but more complex structures will have problems since the depth check is so simple. A check on the key type like cjm did is better if you are sure that there are no deeper keys of that type. Or a combination, like:
my $sorter = sub {
my $h = $_[0];
# just sort by the keys
return $JSON::PP::a cmp $JSON::PP::b
unless $JSON::PP::a =~ /^\d+\z/
&& $JSON::PP::b =~ /^\d+\z/
&& ref($h->{$JSON::PP::a}) eq 'HASH'
&& ref($h->{$JSON::PP::b}) eq 'HASH';
# sort by titles
return $h->{$JSON::PP::a}{title} cmp $h->{$JSON::PP::b}{title};
};
精彩评论