ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

Unity - LINQについて③ -

初めに

 C# を使う上で便利な LINQ についてまとめます。コレクション(配列や List など)の要素を処理するメソッドを集めたものになります。配列や List の並び替えや異なる配列などを合体させるときに使う関数をまとめます。

LINQ とは

 LINQ とは、C# で使われる統合言語クエリ(LINQ: Language-Integrated Query)と呼ばれるものになります。配列や List などの用をを処理する関数を集めたものです。

LINQ の使い方

 下記の文を名前空間(一番上)に追加すれば使うことができるようになります。

using System.Linq;

以前紹介した関数

fineworks-fine.hatenablog.com

(紹介した関数の一覧)

  • All
  • Any
  • Contains
  • OfType
  • SequenceEqual
  • Count
  • First、FirstOrDefault
  • Last、LastOrDefault
  • Single、SingleOrDefault
  • ElementAt、ElementAtDefault
  • Skip、SkipWhile
  • Take、TakeWhile
  • DefaultIfEmpty
  • Where

fineworks-fine.hatenablog.com

(紹介した関数の一覧)

  • Max、Min
  • Sum、Average
  • Aggregate
  • ToArray
  • ToList
  • ToDictionary
  • AsEnumerable
  • Cast
  • Reverse
  • Distinct
  • Select
  • SelectMany

LINQ の関数

OrderBy、OrderByDescending

 配列や List 内の要素を昇順、降順に並び替える場合に使います。OrderBy は降順、OrderByDescending は昇順になります。

void Start()
    {
      int[] number = {4, 3, 6, 9, 1};

      var result1 = number.OrderBy(x => x);
     //1, 3, 4, 6, 9

      var result2 = number.OrderByDescending(x => x);
     //9, 6, 4, 3, 1
    }
ThenBy、ThenByDescending

 要素を並び替えた後、追加の条件で昇順に並べ替えます。

private List<hoge> hL_ = new List<hoge>();

    void Start()
    {
      hL_.Add(new hoge { No=3, Name="五十嵐", Sex="M"});
      hL_.Add(new hoge { No =2, Name = "伊藤", Sex="F"});
      hL_.Add(new hoge { No =1, Name = "山本源流斎", Sex="M"});

      var result = hL_.OrderBy(x => x.No).ThenByDescending(x => x.Name);
      foreach(hoge i in result)
      {
        Debug.Log(i.No + i.Name + i.Sex);
        //1山本源流斎, 2伊藤F, 3五十嵐M
      }
    }

    public struct hoge
    {
      public int No;
      public string Name;
      public string Sex;
    }
GroupBy

 指定のキーで要素をグループ化します。

      Pet[] pets = {
	       new Pet { Name = "Pochi", Age = 4 },
	       new Pet { Name = "Tama", Age = 8 },
	       new Pet { Name = "Nyanko", Age = 1 },
	       new Pet { Name = "Wanko", Age = 1 }
      };
      var result = pets.GroupBy(e => e.Age);
      // {Key = 4, Source = {{ Name = "Pochi", Age = 4 }},
      // Key = 8, Source = {{ Name = "Tama", Age = 8 }},
      // Key = 1, Source = {{ Name = "Nyanko", Age = 1 }, { Name = "Wanko", Age = 1 }}}
    }

    public class Pet
    {
      public string Name { get; set; }
      public int Age { get; set; }
    }
ToLookUp

 特定のデータをキーにしてグループ分けします。

void Start()
    {
        Pet[] pets = {
	       new Pet { Kind = "犬", Name = "Pochi", Age = 4 },
	       new Pet { Kind = "猫", Name = "Tama", Age = 8 },
	       new Pet { Kind = "猫", Name = "Nyanko", Age = 1 },
	       new Pet { Kind = "犬", Name = "Wanko", Age = 1 }
      };
      var result = pets.ToLookup(e => e.Kind);

      foreach(var i in result)
      {
        Debug.Log(i.Key);
        //犬, 猫
        var item = i.ToList();
        foreach(var j in item)
        {
          Debug.Log(j.Kind + j.Name + j.Age);
          //犬Pochi4, 犬Wanko1, 猫Tama8, 猫Nyanko1
        }
      }
    }

    public class Pet
    {
      public string Kind {get; set; }
      public string Name { get; set; }
      public int Age { get; set; }
    }
Concat

 2つのシーケンスを連結します。

void Start()
    {
        Pet[] pets1 = {
	       new Pet { Kind = "犬", Name = "Pochi", Age = 4 },
	       new Pet { Kind = "猫", Name = "Tama", Age = 8 },
	       new Pet { Kind = "猫", Name = "Nyanko", Age = 1 },
	       new Pet { Kind = "犬", Name = "Wanko", Age = 1 }
      };

      Pet[] pets2 = {
       new Pet { Kind = "犬", Name = "Inu", Age = 10 },
       new Pet { Kind = "猫", Name = "Neko", Age = 12 },
    };
      var result = pets1.Concat(pets2);

      foreach(var i in result)
      {
        Debug.Log(i.Kind + i.Name + i.Age);
        //犬Pochi4, 猫Tama8, 猫Nyanko1, 犬Wanko1, 犬Inu10, 猫Neko12
      }
    }

    public class Pet
    {
      public string Kind {get; set; }
      public string Name { get; set; }
      public int Age { get; set; }
    }
Zip

 複数の配列や List を合体させます。

void Start()
    {
      string[] names  = {"あああ", "いいい", "ううう"};
      int[] hp = {4, 2, 3};

      var result = names.Zip(hp, (name, h) => new {Name = name, hp = h});

      foreach(var k in result)
      {
        Debug.Log(k.Name + k.hp);
        //あああ4, いいい2, ううう3
      }
    }

    public class Pet
    {
      public string Kind {get; set; }
      public string Name { get; set; }
      public int Age { get; set; }
    }

 要素数の違うものを合体させる場合、要素数の少ないほうに合わせられます。

Join

 一致するキーに基づいて、2つの配列を結合します。

void Start()
{
  CharaName[] charaname = {
    new CharaName { Id = 1, Name = "あああ"},
    new CharaName { Id = 2, Name = "いいい"},
    new CharaName { Id = 3, Name = "ううう"},
  };

  CharaAge[] charaage = {
    new CharaAge { Id = 1, Age = 12},
    new CharaAge { Id = 2, Age = 8},
    new CharaAge { Id = 3, Age = 22},
  };

   var result = charaname.Join(charaage, name => name.Id, age => age.Id, (name, age) => new {name.Id, name.Name, age.Age});

   foreach(var i in result)
   {
      Debug.Log(i.Id + i.Name + i.Age);
      //1あああ12, 2いいい8, 3ううう22
   }
 }

public class CharaName
{
   public int Id {get; set; }
   public string Name { get; set; }
}

public class CharaAge
{
   public int Id {get; set; }
   public int Age { get; set; }
}
GroupJoin

 Join によるデータの結合と GroupBy によるグループ化をまとめて行うことができます。

void Start()
{
  CharaName[] charaname = {
  	       new CharaName { Id = 1, Name = "あああ"},
  	       new CharaName { Id = 2, Name = "いいい"},
  	       new CharaName { Id = 3, Name = "ううう"},
        };

      CharaAge[] charaage = {
          new CharaAge { Id = 1, Age = 12},
          new CharaAge { Id = 2, Age = 8},
          new CharaAge { Id = 3},
  };

      var result = charaname.GroupJoin(charaage, name => name.Id, age => age.Id, (name, age) => new {Id = name.Id, name = name.Name, age = age.DefaultIfEmpty() })
			.SelectMany(x => x.age, (x, age) => new
			{
				Id = x.Id,
				Name = x.name,
				age = age != null ? age.Age : -1
			});

      foreach(var i in result)
      {
        Debug.Log(i.Id + i.Name + i.age);
        //1あああ12, 2いいい8, 3ううう0
      }
 }

    public class CharaName
    {
      public int Id {get; set; }
      public string Name { get; set; }
    }

    public class CharaAge
    {
      public int Id {get; set; }
      public int Age { get; set; }
    }
Union, Except, Intersect

 Union は2つの和集合、Except は差集合、Intersect は積集合を生成します。

int[] num1 = new[] {1, 3, 5, 7};
int[] num2 = new[] {1, 2, 7, 9};

var resultUnion = num1.Union(num2);
//1, 3, 5, 7, 2, 9
var resultExcept = num1.Except(num2);
//3, 5
var resultIntersect = num1.Intersect(num2);
//1, 7

 和集合は2つの要素を合わせた集合になります。差集合は共通部分を引いた集合になります。積集合は2つの集合の共通部分になります。

Empty

 空の List を生成します。

Enumerable.Empty<型>()

Debug.Log(Enumerable.Empty<int>().Count());
//0

 Union をする際の中間値として利用することがあります。

Repeat

 指定した要素を繰り返したシーケンスを返します。

 // 中身が{ 1, 1, 1, 1 } のint[3]
var numArray = Enumerable.Repeat( 1, 4 ).ToArray();

// 中身が{ 2, 2, 2, 2, 2 } のList<int>
var numList = Enumerable.Repeat( 2, 5 ).ToList();

 戻り値そのままだと IEnumerable のため使いづらいです。そのため、ToArray で配列に、ToList で List にしています。

Range

 範囲内の連続した整数のシーケンスを作ります。

var seq1 = Enumerable.Range( 1, 4 ).ToArray();
//1, 2, 3, 4
var seq2 = Enumerable.Range( -10, 5 ).ToArray();
-10, -9, -8, -7, -6

 第1引数は初期値、第2引数は生成する連続した整数の個数になります。

最後に

 戻り値が IEnumerable になるなどして、そのままだと使いづらいです。そのため、ToArray や ToList を併用して使うようにしましょう。