for WPF developers
Home Profile Tips 全記事一覧

Except 拡張メソッドで差集合を取得する

(2017/03/08 17:26:39 created.)

(2017/03/13 8:28:18 modified.)

Except 拡張メソッドは 2 つのシーケンスを比較し、重複した要素を除外します。

Program.cs
  1. namespace Tips_Linq
  2. {
  3.     using System;
  4.     using System.Linq;
  5.  
  6.     class Program
  7.     {
  8.         static void Main(string[] args)
  9.         {
  10.             var numbers1 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  11.             var numbers2 = new int[] {        3, 4, 5, 6, 7           };
  12.  
  13.             var numbers = numbers1.Except(numbers2);
  14.             Console.WriteLine("コレクションの要素は {{ {0} }} です。", string.Join(", ", numbers));
  15.  
  16.             Console.ReadKey();
  17.         }
  18.     }
  19. }


次のような Person クラスに対して Except 拡張メソッドを使うこともできます。

Person.cs
  1. namespace Tips_Linq
  2. {
  3.     using System;
  4.  
  5.     /// 
  6.     /// 人物データを表します。
  7.     /// 
  8.     public class Person
  9.     {
  10.         /// 
  11.         /// 氏名を取得または設定します。
  12.         /// 
  13.         public string Name { get; set; }
  14.  
  15.         /// 
  16.         /// 更新日付を取得または設定します。
  17.         /// 
  18.         public DateTime Date { get; set; }
  19.     }
  20. }

例えば Name プロパティの値が同じものを除外したい場合、Name プロパティで比較するための比較子を用意します。詳細は「Distinct 拡張メソッドで重複する要素をシーケンスから除外する」で説明しているため割愛します。

PersonComparer.cs
  1. namespace Tips_Linq
  2. {
  3.     using System.Collections.Generic;
  4.  
  5.     /// 
  6.     /// Person クラスに対する等値比較子を表します。
  7.     /// 
  8.     public class PersonComparer : IEqualityComparer<Person>
  9.     {
  10.         public static readonly PersonComparer DateComparer = new PersonComparer();
  11.  
  12.         /// 
  13.         /// 指定された Person クラスのオブジェクトが等しいかどうかを確認します。
  14.         /// 
  15.         /// "x">比較基準を指定します。
  16.         /// "y">比較対象を指定します。
  17.         /// Date プロパティが等しい場合に true を返します。
  18.         public bool Equals(Person x, Person y)
  19.         {
  20.             return x.Date == y.Date;
  21.         }
  22.  
  23.         /// 
  24.         /// ハッシュ値を取得します。
  25.         /// 
  26.         /// "obj">ハッシュ値を算出するオブジェクトを指定します。
  27.         /// 算出したハッシュ値を返します。
  28.         public int GetHashCode(Person obj)
  29.         {
  30.             return obj.Date.GetHashCode();
  31.         }
  32.     }
  33. }

この比較子を使用することで、Name プロパティ値が同じの要素を除外することができます。

Program.cs
  1. namespace Tips_Linq
  2. {
  3.     using System;
  4.     using System.Collections.Generic;
  5.     using System.Linq;
  6.  
  7.     class Program
  8.     {
  9.         static void Main(string[] args)
  10.         {
  11.             Console.WriteLine("== 対象とするシーケンスの内容 =====");
  12.             var people1 = GetPeople1();
  13.             foreach (var person in people1)
  14.             {
  15.                 Console.WriteLine(person.Name);
  16.             }
  17.  
  18.             Console.WriteLine("== 差を取るシーケンスの内容 =====");
  19.             var people2 = GetPeople2();
  20.             foreach (var person in people2)
  21.             {
  22.                 Console.WriteLine(person.Name);
  23.             }
  24.  
  25.             Console.WriteLine("== 差を取った後のシーケンスの内容 =====");
  26.             var newPeople = people1.Except(people2, PersonComparer.NameComparer);
  27.             foreach (var person in newPeople)
  28.             {
  29.                 Console.WriteLine(person.Name);
  30.             }
  31.  
  32.             Console.ReadKey();
  33.         }
  34.  
  35.         /// 
  36.         /// 人物コレクションの列挙子を取得します。
  37.         /// 
  38.         /// 
  39.         static IEnumerable<Person> GetPeople1()
  40.         {
  41.             yield return new Person() { Name = "田中 淳平", Date = new DateTime(2011, 5, 2) };
  42.             yield return new Person() { Name = "鈴木 ほのか", Date = new DateTime(2014, 3, 24) };
  43.             yield return new Person() { Name = "小池 哲司", Date = new DateTime(2002, 6, 13) };
  44.             yield return new Person() { Name = "恩田 進", Date = new DateTime(1996, 9, 27) };
  45.             yield return new Person() { Name = "中津山 亜希子", Date = new DateTime(2015, 11, 28) };
  46.         }
  47.  
  48.         /// 
  49.         /// 人物コレクションの列挙子を取得します。
  50.         /// 
  51.         /// 
  52.         static IEnumerable<Person> GetPeople2()
  53.         {
  54.             yield return new Person() { Name = "田中 淳平", Date = new DateTime(2016, 12, 1) };
  55.             yield return new Person() { Name = "小池 哲司", Date = new DateTime(2011, 7, 1) };
  56.         }
  57.     }
  58. }