2015年12月4日金曜日

.Net(VB C#) LINQ
Enumerable.OrderBy メソッド、ThenBy メソッド

Enumerable.OrderBy メソッド
【C#】
シーケンスの要素をキーに従って昇順に並べ替えます。
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>
    (this IEnumerable source, Func keySelector)


指定された比較子を使用してシーケンスの要素を昇順に並べ替えます。
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>:
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
【VB】
シーケンスの要素をキーに従って昇順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderBy(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey)) 
    As IOrderedEnumerable(Of TSource)

指定された比較子を使用してシーケンスの要素を昇順に並べ替えます。
<ExtensionAttribute>
Public Shared Function OrderBy(Of TSource, TKey) 
    (source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey), comparer As IComparer(Of TKey))
    As IOrderedEnumerable(Of TSource)

Enumerable.ThenBy メソッド
【C#】
キーに従って、シーケンス内の後続の要素を昇順で配置します。
ThenBy<TSource, TKey>(IOrderedEnumerable<TSource>, Func<TSource, TKey>)

指定された比較子を使用して、シーケンス内の後続の要素を昇順で配置します。
ThenBy<TSource, TKey>(IOrderedEnumerable<TSource>, Func<TSource, TKey>, IComparer<TKey>)
【VB】
キーに従って、シーケンス内の後続の要素を昇順で配置します。
ThenBy(Of TSource, TKey)(IOrderedEnumerable(Of TSource), Func(Of TSource, TKey))

指定された比較子を使用して、シーケンス内の後続の要素を昇順で配置します。
ThenBy(Of TSource, TKey)(IOrderedEnumerable(Of TSource), Func(Of TSource, TKey), IComparer(Of TKey))

簡単に言うと…
シーケンス(配列やコレクションなど)の各要素を昇順に並び替える。

データを並び替える項目が一つ目ならOrderByを使用しします。
二つ目からはThenByを使用します。

このメソッドは遅延実行されます。


テストデータです。
【C#】
private class Fruit
{
    public string Name { get; set; }
    public string Rank { get; set; }
    public decimal Price { get; set; }
}

var fruits = new List<Fruit>()
                {
                    new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 600 },
                    new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 },
                    new Fruit(){Name = "りんご", Rank = "B" , Price = 800 },
                    new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }
                };
【VB】
Private Class Fruit
    Public Property Name As String
    Public Property Rank As String
    Public Property Price As Decimal
End Class

Dim fruits = New List(Of Fruit)() From
            {
                New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600},
                New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200},
                New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800},
                New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}
            }

果物リストを名前で昇順に並び替えます。

【C#】
//名称を昇順で並び替え
var ordered = fruits.OrderBy(itm => itm.Name);

//出力
foreach (var itm in ordered)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=600
//名称=みかん, ランク=A, 値段=500
//名称=りんご, ランク=A, 値段=1,000
//名称=りんご, ランク=B, 値段=800
【VB】
'名称を昇順で並び替え
Dim ordered = fruits.OrderBy(Function(itm) itm.Name)

'出力
For Each itm In ordered
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=600
'名称=みかん, ランク=A, 値段=500
'名称=りんご, ランク=A, 値段=1,000
'名称=りんご, ランク=B, 値段=800

果物リストを名前の昇順、値段の昇順に並び替えます。
一つ目のソート条件である名前はOrderByメソッドで指定し、二つ目のソート条件である値段はThenByメソッドで指定します。

【C#】
//名称の昇順、値段の昇順で並び替え
var ordered = fruits.OrderBy(itm => itm.Name).ThenBy(itm => itm.Price);

//出力
foreach (var itm in ordered)
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));  
//名称=ぶどう, ランク=B, 値段=1,200
//名称=みかん, ランク=A, 値段=500
//名称=みかん, ランク=A, 値段=600
//名称=りんご, ランク=B, 値段=800
//名称=りんご, ランク=A, 値段=1,000
【VB】
'名称の昇順、値段の昇順で並び替え
Dim ordered = fruits.OrderBy(Function(itm) itm.Name).ThenBy(Function(itm) itm.Price)

'出力
For Each itm In ordered
    Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"))
Next
'名称=ぶどう, ランク=B, 値段=1,200
'名称=みかん, ランク=A, 値段=500
'名称=みかん, ランク=A, 値段=600
'名称=りんご, ランク=B, 値段=800
'名称=りんご, ランク=A, 値段=1,000


今まで使う必要に迫られたことはないのですが…
OrderByメソッドやThenByメソッドの第二引数にICompareを指定することで、独自の並び替え方法でソートすることができます。

例えば「2015/1/1, 2015/11/11, 2015/5/31, 2015/4, 20」といった日付を表す文字列があったとします。
これらを文字列としてソートすると「20, 2015/1/1, 2015/11/11, 2015/4, 2015/5/31」となります。
日付と考えるのであれば「20, 2015/1/1, 2015/4, 2015/5/31, 2015/11/11」と並んで欲しいです。
このようなとき独自のソート方法を定義します。

独自のソート方法を定義するには
IComparerインターフェイスを実装したクラスを作成し、Compareメソッドに比較方法を記述します。
【C#】
private class CompareDateString : System.Collections.Generic.IComparer<string>
{
    public int Compare(string x, string y)
    {
        //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返すようにする
        string[] aryX = x.Split('/');
        string[] aryY = y.Split('/');

        string xDateString;
        xDateString = aryX[0].PadLeft(4, '0');
        xDateString += (aryX.Length > 1 ? aryX[1].PadLeft(2, '0') : "00");
        xDateString += (aryX.Length > 2 ? aryX[2].PadLeft(2, '0') : "00");

        string yDateString;
        yDateString = aryY[0].PadLeft(4, '0');
        yDateString += (aryY.Length > 1 ? aryY[1].PadLeft(2, '0') : "00");
        yDateString += (aryY.Length > 2 ? aryY[2].PadLeft(2, '0') : "00");

        int i = xDateString.CompareTo(yDateString);
        return i;
    }
}
【VB】
    Private Class CompareDateString : Implements System.Collections.Generic.IComparer(Of String)

        Public Function Compare(x As String, y As String) As Integer _
            Implements System.Collections.Generic.IComparer(Of String).Compare
            
            'xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返すようにする
            Dim aryX As String() = x.Split("/"c)
            Dim aryY As String() = y.Split("/"c)

            Dim xDateString As String
            xDateString = aryX(0).PadLeft(4, "0")
            xDateString += If(aryX.Length > 1, aryX(1).PadLeft(2, "0"), "00")
            xDateString += If(aryX.Length > 2, aryX(2).PadLeft(2, "0"), "00")

            Dim yDateString As String
            yDateString = aryY(0).PadLeft(4, "0")
            yDateString += If(aryY.Length > 1, aryY(1).PadLeft(2, "0"), "00")
            yDateString += If(aryY.Length > 2, aryY(2).PadLeft(2, "0"), "00")

            Dim i As Integer = xDateString.CompareTo(yDateString)
            Return i
        End Function

    End Class
OrderyByメソッドの第二引数にICompareを実装したCompareDateStringを指定します。
【C#】
string[] dateStrings = { "2015/1/1", "2015/11/11", "2015/5/31", "2015/4", "20" };

//普通に昇順でソート
var orderedList1 = dateStrings.OrderBy(str => str);
//出力
foreach (var itm in orderedList1)
    Console.WriteLine(itm);
//20
//2015/1/1
//2015/11/11
//2015/4
//2015/5/31

//第二引数にICompareを実装したCompareDateStringを指定しソート
var orderedList2 = dateStrings.OrderBy(str => str, new CompareDateString());
//出力
foreach (var itm in orderedList2)
    Console.WriteLine(itm);
//20
//2015/1/1
//2015/4
//2015/5/31
//2015/11/11
【VB】
Dim dateStrings As String() = {"2015/1/1", "2015/11/11", "2015/5/31", "2015/4", "20"}

'普通に昇順でソート
Dim orderedList1 = dateStrings.OrderBy(Function(str) str)

For Each itm In orderedList1
    Console.WriteLine(itm)
Next
'20
'2015/1/1
'2015/11/11
'2015/4
'2015/5/31

'第二引数にICompareを実装したCompareDateStringを指定しソート
Dim orderedList2 = dateStrings.OrderBy(Function(str) str, New CompareDateString())

For Each itm In orderedList2
    Console.WriteLine(itm)
Next
'20
'2015/1/1
'2015/4
'2015/5/31
'2015/11/11
※VBの場合、コンパイルオプションで「Option Infer」をONにし、型推論を有効にしてください。

.Net(VB C#) LINQのメソッド一覧

0 件のコメント: