2015年6月5日

LINQ OrderByDescending != OrderBy + Reverse

LINQ でシーケンスを降順にソートする場合、素直に OrderByDescending を使う方法と、OrderByReverse を組み合わせる方法がある。
(降順ソート用 IComparer<T> を使う方法もあるが、OrderByDescending と同じなので割愛)

この 2 つの方法は、異なる結果になることがある。
理由は、OrderByDescending と OrderBy が両方とも 安定ソートであるため。
「降順の安定ソートってどっち?」と混乱するかもしれないが、OrderByDescending の実装は MSDN の記載通り同じキーを持つ要素の順序は保持される。
OrderBy も同様なので、同じキーを持つ要素については、OrderByDescending と OrderBy は同じ順序になってしまう。

つまり、両者は正反対の順序にならないため、OrderBy + Reverse は必ずしも OrderByDescending の結果と一致しない。
※並び替えキーに重複がない場合は一致する。
※要素=並び替えキーとなる場合は、順序が違っても分からないため、考慮不要

[不一致ケース]

// インデックス付き配列を並べ替え
var data = new[]{ 'a', 'b', 'a', 'c', 'a' }.Select((V, I) => new{ V, I });

Console.WriteLine("[OrderBy]");
Console.WriteLine(string.Join(" ", data.OrderBy(v => v.V)));

Console.WriteLine("[OrderBy + Reverse]");
Console.WriteLine(string.Join(" ", data.OrderBy(v => v.V).Reverse()));

Console.WriteLine("[OrderByDescending]");
Console.WriteLine(string.Join(" ", data.OrderByDescending(v => v.V)));
[結果]
[OrderBy]
{ V = a, I = 0 } { V = a, I = 2 } { V = a, I = 4 } { V = b, I = 1 } { V = c, I = 3 }
[OrderBy + Reverse]
{ V = c, I = 3 } { V = b, I = 1 } { V = a, I = 4 } { V = a, I = 2 } { V = a, I = 0 }
[OrderByDescending]
{ V = c, I = 3 } { V = b, I = 1 } { V = a, I = 0 } { V = a, I = 2 } { V = a, I = 4 }

0 件のコメント:

コメントを投稿