2つのリストの違い


118

CustomsObjectsで満たされた2つの汎用リストがあります。

3つのリストで、これら2つのリスト(最初のリストにあり、2番目のリストにあるアイテムがないアイテム)の違いを取得する必要があります。

使うの.Except()はいい考えだと思っていましたが、使い方がわかりません。

c# 

回答:


237

使用するのExceptは、まさに正しい方法です。タイプがEqualsandをオーバーライドする場合GetHashCode、または参照タイプの等価性のみに関心がある場合(つまり、2つの参照がまったく同じオブジェクトを参照している場合は、「等しい」のみ)、次のように使用できます。

var list3 = list1.Except(list2).ToList();

たとえばIDによって等号のカスタムアイデアを表現する必要がある場合は、を実装する必要がありますIEqualityComparer<T>。例えば:

public class IdComparer : IEqualityComparer<CustomObject>
{
    public int GetHashCode(CustomObject co)
    {
        if (co == null)
        {
            return 0;
        }
        return co.Id.GetHashCode();
    }

    public bool Equals(CustomObject x1, CustomObject x2)
    {
        if (object.ReferenceEquals(x1, x2))
        {
            return true;
        }
        if (object.ReferenceEquals(x1, null) ||
            object.ReferenceEquals(x2, null))
        {
            return false;
        }
        return x1.Id == x2.Id;
    }
}

次に使用します:

var list3 = list1.Except(list2, new IdComparer()).ToList();

これにより、重複する要素が削除されることに注意してください。複製を保持する必要がある場合は、おそらくlist2次のようなものからセットを作成して使用するのが最も簡単です。

var list3 = list1.Where(x => !set2.Contains(x)).ToList();

18
余談ですExceptが、これをSetオペレーションとして追加すると、結果のリストには異なる値{'A','A','B','C'}.Except({'B','C'}) が返されます(例:戻り値){'A'}
digEmAll

4
私はこれを持っています:{'A'、 'A'、 'B'、 'C​​'}。Except({'B'、 'C​​'})は{'A'}を返します...これは機能しますが、{'B' 、 'C'}。Except({'A'、 'B'、 'C​​'})は{'A'}を返しません
MrLister

2
2つのセットのセットの差は、2番目のセット(MSを引用)に表示されない最初のセットのメンバーとして定義されます。したがって、BとCは両方とも2番目のセットにあるため、{B、C} .Except({A、B、C})は何も返さないはずです。バグではありません。関数の数学的定義に関係しています。
スティーブヒバート2017

したがって、@ JonSkeet 2つのプロパティに基づいて2つのリストを比較したい場合、たとえば、2つのプロパティをこの方法で比較して、等しくない項目を取得できますか?
Ehsan Sajjad

1
@NetMage:OPは、「最初のものにはアイテムがあり、2番目のものにはアイテムがない」を望んでいると述べました。最初のリストに{5、5、5、5、1}が含まれ、2番目のリストに{5}が含まれている場合、最初のリストには1のみが含まれ、2番目のリストには含まれません。
Jon Skeet

72

あなたはこのようなことをすることができます:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));

それは私に1つのforeachループを保存しました。
alice7

12
それらの「l.someproperty」の1つが「p.someproperty」であるべきではありませんか?
Manos Dilaverakis 2013年

6
これは、customlist.Where(p => otherlist.Any(l => p.someproperty!= l.someproperty));で簡略化できます。
Dhanuka777 2016年

@ Dhanuka777不正解です。そのようにしたい場合はAnyAllこれは、2番目のリストに最初のリストのアイテムが含まれる可能性があるためp.someproperty != l.somepropertyです。ただし、最初のリストがチェックされていない場合は、すぐにtrueになります。これにより、両方のリストに存在するアイテムが返されます。これに賛成した6人の恥。
Murphybro2

26

私は強調することが重要だと思います-Exceptメソッドを使用すると、最初のアイテムのみが返され、2番目のアイテムのみが返されます。最初に表示されない2番目の要素は返しません。

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2

しかし、あなたが2つのリストの真の違いを取得したい場合:

最初のアイテムに2番目のアイテムがないアイテムと2番目のアイテムに最初のアイテムがないアイテム。

Exceptを2回使用する必要があります。

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7

または、HashSetのSymmetricExceptWithメソッドを使用できます。しかし、それは呼び出されたセットを変更します:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7

それは役に立ちます。OPが完全な違いを望まないことを明確にしたことに注意してください。「これら2つのリスト(最初のリストにあるアイテム、2番目のリストにあるアイテムなし)の違いを取得する必要があります」。
rsenna

10
var third = first.Except(second);

ToList()後で呼び出すこともできますExcept()遅延コレクションを参照したくない場合は、のにます。)

Except()値が比較される場合、メソッドは、既定の比較を使用して値を比較するような塩基のデータ・タイプのものであるintstringdecimal

そうでない場合、比較はオブジェクトアドレスによって行われますが、これはおそらく必要なことではありません...その場合は、カスタムオブジェクトを実装しますIComparable(またはカスタムIEqualityComparerを実装してExcept()メソッドに渡します)。


3
var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();

注:list3両方のリストにないアイテムまたはオブジェクトが含まれます。注:ToList()そうではありませんtoList()


1

Except拡張メソッドは2つのIEumerableで動作するため、O(n ^ 2)操作になるように思えます。パフォーマンスに問題がある場合(リストが大きい場合)、list1からHashSetを作成し、HashSetのExceptWithメソッドを使用することをお勧めします。


9
Enumerable.Except内部で使用するHashSetか、または類似したもの。それは間違いなくナイーブO(n ^ 2)アルゴリズムを使用しません。
ジムミッシェル

@ジム・ミッシェル:そうです、Enumerable.Exceptは内部のSetデータ構造を使用し、両方のIEnumerableからアイテムをセットに追加します。ドキュメンテーションがそれについて何か言うことを望みます。
foson

1

これが私の解決策です:

    List<String> list1 = new List<String>();

    List<String> list2 = new List<String>();

    List<String> exceptValue = new List<String>();

foreach(String L1 in List1) 
{
    if(!List2.Contains(L1)
    {
         exceptValue.Add(L1);
    }
}
foreach(String L2 in List2) 
{
    if(!List1.Contains(L2)
    {
         exceptValue.Add(L2);
    }
}

-1

少し遅れましたが、ここで私のための実用的な解決策があります

 var myBaseProperty = (typeof(BaseClass)).GetProperties();//get base code properties
                    var allProperty = entity.GetProperties()[0].DeclaringType.GetProperties();//get derived class property plus base code as it is derived from it
                    var declaredClassProperties = allProperty.Where(x => !myBaseProperty.Any(l => l.Name == x.Name)).ToList();//get the difference

上記のコードで、基本クラスと派生クラスのリストのプロパティの違いを取得しています


-1
var resultList = checklist.Where(p => myList.All(l => p.value != l.value)).ToList();

注:結果リストは、チェックリストには存在するがmyListには存在しないアイテムです
Teezy7

-2
List<ObjectC> _list_DF_BW_ANB = new List<ObjectC>();    
List<ObjectA> _listA = new List<ObjectA>();
List<ObjectB> _listB = new List<ObjectB>();

foreach (var itemB in _listB )
{     
    var flat = 0;
    foreach(var itemA in _listA )
    {
        if(itemA.ProductId==itemB.ProductId)
        {
            flat = 1;
            break;
        }
    }
    if (flat == 0)
    {
        _list_DF_BW_ANB.Add(itemB);
    }
}

回答が承認されてから6年後、価値のある新しいものは何も追加されません
ミッチウィート

-3

両方のリストがIEnumerableインターフェイスを実装している場合は、LINQを使用してこれを実現できます。

list3 = list1.where(i => !list2.contains(i));

6
また、リストにそれぞれ100万個のアイテムが含まれている場合、長い間待機することになります。IEnumerable.Except代わりに使用してください。
ジムミッシェル

3
うん。約450点しかないので、まだ待っています。これを使用するように注意してください。
Marnee KG7SIO 2016年

-3
        List<int> list1 = new List<int>();
        List<int> list2 = new List<int>();
        List<int> listDifference = new List<int>();

        foreach (var item1 in list1)
        {
            foreach (var item2 in list2)
            {
                if (item1 != item2)
                    listDifference.Add(item1);
            }
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.