読者です 読者をやめる 読者になる 読者になる

NeverBlog::Likk::Unexistable;

見なかったことにして下さい

Perl で undef を含むものをsortについて考えてみた。

blog.likk.jp
で切り捨てた undefについて考えてみた。undef sortした時に最大値ととるか最小値ととるかによって違うけど、最小値として考えるなら以下のとおりにの書き方になる。

use strict;
use warnings;
use Clone qw/clone/;
use YAML;

my $list = [
    { name => 'aaa', hoge => 10,    fuga => 1 },
    { name => 'bbb', hoge => 11,    fuga => 2 },
    { name => 'ccc', hoge => 11,    fuga => 1 },
    { name => 'ddd', hoge => 12,    fuga => 1 },
    { name => 'eee', hoge => undef, fuga => undef },
    { name => 'fff', hoge => undef, fuga => 1 },
    { name => 'ggg', hoge => 10,    fuga => undef },
    { name => 'hhh', }
];

my $sorted_list = [
    map  { sprintf("%s, hoge:%s fuga:%s", $_->{name} || '' , $_->{hoge} || '', $_->{fuga} ||'') }
    sort {
      (defined($a->{hoge} and $b->{hoge}) ? $a->{hoge} <=> $b->{hoge}
        : defined($a->{hoge}) ? +1
        : defined($b->{hoge}) ? -1 : 0
      )
      or
      (defined($a->{fuga} and $b->{fuga}) ? $a->{fuga} <=> $b->{fuga}
        : defined($a->{fuga}) ? +1
        : defined($b->{fuga}) ? -1 : 0
      )
    }
    map {
        $_->{hoge} //=undef;
        $_->{fuga} //=undef;
        $_;
    } @{ clone($list) }
];

warn YAML::Dump $sorted_list;
#---
#- 'eee, hoge: fuga:'
#- 'hhh, hoge: fuga:'
#- 'fff, hoge: fuga:1'
#- 'ggg, hoge:10 fuga:'
#- 'aaa, hoge:10 fuga:1'
#- 'ccc, hoge:11 fuga:1'
#- 'bbb, hoge:11 fuga:2'
#- 'ddd, hoge:12 fuga:1'

ちなみに、defind とかしなくて普通に <=> してもwarning 吐きながらではあるが同じ結果にはなる。
昇順だけど、undef は最後に出したいとかなら上記の例の符号を逆にすればいい。

そもそもsortしたいカラムならNOT NULL制約かけろよとかデフォルト値設けろという話はとりあえず置いておく。


ところで、同じことしてくれるCPANモジュールあると思うんですが、誰か知りませんか。