プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2019/06/03

[C#] Linq でインデックスを参照する場合の注意点

update2019/06/12 event_note2019/06/03 3:58

Linq でインデックスを参照する場合、Where と Select の順番には少し注意が必要です。

サンプルコード

public static void Main()
{
    var persons = new List<Person>()
    {
        new Person{ Name = "Foo", Country = "Japan", Age = 10},
        new Person{ Name = "Bar", Country = "Japan", Age = 20},
        new Person{ Name = "Hoge", Country = "America", Age = 10},
        new Person{ Name = "Piyo", Country = "America", Age = 20},
        new Person{ Name = "Fuga", Country = "America", Age = 30},
    };

    foreach (var (p, i) in
        persons
            .Where(x => x.Country == "America")
            .Where(x => x.Age > 15)
            .Select((p, i) => (p, i)))
    {
        Console.WriteLine($"[{i}] {p.Name}, {p.Country}, {p.Age}");
    }
    Console.ReadKey();
}

public class Person
{
    public string Name { get; set; }
    public string Country { get; set; }
    public int Age { get; set; }
}

出力結果

[0] Piyo, America, 20
[1] Fuga, America, 30

persons の 3 番目と 4 番目の値が出力されますが、出力されたインデックスは 0 と 1 になっています。
これは、Where でフィルタリングした結果に対して Select しているためです。

分かっている人にとっては当たり前ですけどね。

元のインデックスを取得したい場合は?

for 文を使うとかありますけど、Linq でやるならば私が思いついたのは以下のようなコードです。

Select を先に持ってくる

Select を先に持ってくると Where でフィルタリングされる前なので、元のインデックスとなります。
タプルを使うので、C# 7.1 以上で使えます。

public static void Main()
{
    var persons = new List<Person>()
    {
        new Person{ Name = "Foo", Country = "Japan", Age = 10},
        new Person{ Name = "Bar", Country = "Japan", Age = 20},
        new Person{ Name = "Hoge", Country = "America", Age = 10},
        new Person{ Name = "Piyo", Country = "America", Age = 20},
        new Person{ Name = "Fuga", Country = "America", Age = 30},
    };

    foreach (var (p, i) in
        persons
            .Select((p, i) => (p, i))
            .Where(t => t.p.Country == "America")
            .Where(t => t.p.Age > 15))
    {
        Console.WriteLine($"[{i}] {p.Name}, {p.Country}, {p.Age}");
    }
    Console.ReadKey();
}

public class Person
{
    public string Name { get; set; }
    public string Country { get; set; }
    public int Age { get; set; }
}

出力結果

[3] Piyo, America, 20
[4] Fuga, America, 30

参考 URL