- ベストアンサー
2007/1/15形式をソートしたい
予約カレンダーを作っています。 ユーザーは不特定の日を予約できます。 CSVファイル 2,1029,2007/1/15,C, 3,1029,2007/1/15,B,checked 4,1029,2007/1/10,D, 5,1029,2007/1/9,C,checked 6,1029,2007/1/16,D, 8,1023,2007/1/17,D 9,1023,2007/1/24,D 10,1023,2007/1/24,C 11,1023,2007/1/10,D ID,会員番号,日付,ステータス,承認 ソートがうまくいかず上記のように並んでいます。 理想としては 9,1023,2007/1/24,D 10,1023,2007/1/24,C 8,1023,2007/1/17,D 11,1023,2007/1/10,D のように日付が新しい方を上にして書き込みたいのです。 2007/1/24のところのソートが上手くいきません。 また、IDの順序も変わると新しいIDをつけるときに困りそうです。 なにかいい方法があったら教えてください。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
Perlなんでいくらでも小難しく書けるのですが(シュオーツ変換とかで検索してみてください)、 できるだけわかりやすくなるように書いてみました。 #1の方の挙げられたスクリプトはsortのブロックの中で色々処理をしているので、 ソートに伴う比較の回数だけそれを行うことになるので、あまり速度面ではよろしくありません。 こういう場合、ソートのキーとなる部分を取り出して別個に配列に仕立て上げて 処理するのがよくある手段です(これを突き詰めていくとシュオーツ変換になります)。 ソートのキーが複数あり、それらの間で順位が決まっているなら sort のブロックの中で ・優先順位1のキーで比較 (今回の例では日付) ・比較結果が0以外ならそれを返し ・比較結果が0なら(同じ日付)優先順位2のキーで比較しその結果を返す という手順をとればよいです。 use strict; use warnings; my @records = <DATA>; #make sort key my @primekeys; my @subkeys; foreach my $l (@records) { my ($id, $date); ($id, undef, $date, undef) = split q{,}, $l, 4; my ($y, $m, $d) = split q{/}, $date, 3; push @primekeys, ($y*10000 + $m*100 + $d); push @subkeys, $id; } my @sortedkeys = sort {$primekeys[$b] <=> $primekeys[$a] || $subkeys[$a] <=> $subkeys[$b]} 0 .. $#records; my @sortedrecords = @records[@sortedkeys]; print @sortedrecords; __END__ 2,1029,2007/1/15,C, 3,1029,2007/1/15,B,checked 4,1029,2007/1/10,D, 5,1029,2007/1/9,C,checked 6,1029,2007/1/16,D, 8,1023,2007/1/17,D 9,1023,2007/1/24,D 10,1023,2007/1/24,C 11,1023,2007/1/10,D
その他の回答 (2)
- guci-ok
- ベストアンサー率33% (49/146)
色々な書き方があるかもしれませんが、最近私が書いたものです。 ソートブロックの中が込み入っている場合は、結果を求める関数 を別に作って呼び出すと、コードがすっきりします。 #!Perl use File::Slurp; my @data = read_file("A1.csv"); @data = sort { get_date($b) <=> get_date($a) } @data; print @data; sub get_date { my $rec = shift; my $ymd = (split /,/ => $rec)[2]; sprintf '%04d%02d%02d' => (split /\// => $ymd); } __END__
- okiyoshi
- ベストアンサー率34% (11/32)
> ソートがうまくいかず とあるので、ソート部分だけだと、 ・各行を項目に分割し、 ・日付を年月日に分割してyyyymmdd形式に統一し、 ・それを降順でソート というところがキモでしょうか・・良い方法かどうかは・・? my @data = <CSV>; # @dataにCSVの全行が有るとして chomp @data; # 必要なら各行末尾の改行コードを除く @data = sort{ my $data_a = join '', map{ sprintf("%02d",$_) } split /\//, (split /,/, $a)[2]; my $data_b = join '', map{ sprintf("%02d",$_) } split /\//, (split /,/, $b)[2]; $data_b cmp $data_a # 日付は数字8桁なのでcmpは<=>でも可 } @data;
お礼
no3さんの方法で試したところうまくいきました。 一発で上手くいくとは思わなかったので驚きです。 ただ、no2さん、no1さん、せっかく回答頂いたのに 試さなくてすみません。 でも、アルゴリズムがわかりました。 みなさんありがとうございました。