3

C# にオブジェクトのリストがあります。すべてのオブジェクトには、プロパティ dept と course が含まれています。
同じ学科とコースを持つオブジェクトがいくつかあります。

一意の (学部とコース) プロパティごとにオブジェクトが 1 つしかないリストをトリミングする (または新しいリストを作成する) にはどうすればよいですか。

[追加の重複はリストから削除されます]

私は単一のプロパティでこれを行う方法を知っています:

fooList.GroupBy(x => x.dept).Select(x => x.First());

しかし、複数のプロパティ (2 つ以上) に対してこれを行う方法を知りたいですか?

4

2 に答える 2

6

複数のプロパティを使用するには、匿名型を使用できます。

var query = fooList.GroupBy(x => new { x.Dept, x.Course })
                   .Select(x => x.First());

もちろん、これはどのタイプDeptCourseが等しいかによって決まります。または、クラスを実装してから、比較子を受け入れるメソッドIEqualityComparer<T>を使用することもできます。Enumerable.Distinct

于 2012-04-17T13:38:54.383 に答える
4

もう1つのアプローチは、LINQDistinct拡張メソッドを。と一緒に使用することIEqualityComparer<Foo>です。比較ツールを実装する必要があります。ただし、後者は再利用可能でテスト可能です。

public class FooDeptCourseEqualityComparer : IEqualityComparer<Foo>
{
    public bool Equals(Foo x, Foo y)
    {
        return
            x.Dept == y.Dept &&
            x.Course.ToLower() == y.Course.ToLower();
    }

    public int GetHashCode(Foo obj)
    {
        unchecked {
            return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
        }
    }

    #region Singleton Pattern

    public static readonly FooDeptCourseEqualityComparer Instance =
        new FooDeptCourseEqualityComparer();

    private FooDeptCourseEqualityComparer() { }

    #endregion
}

私の例では、シングルトンパターンを使用しています。クラスには状態情報がないため、使用するたびに新しいインスタンスを作成する必要はありません。

私のコードは値を処理しませんnull。もちろん、発生する可能性がある場合は、それらを処理する必要があります。

一意の値は次のように返されます

var result = fooList.Distinct(FooDeptCourseEqualityComparer.Instance);

アップデート

コンストラクターでラムダ式を受け入れ、複数の状況で再利用できるジェネリックEqualityComparerクラスを使用することをお勧めします

public class LambdaEqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _areEqual;
    private Func<T, int> _getHashCode;

    public LambdaEqualityComparer(Func<T, T, bool> areEqual,
                                  Func<T, int> getHashCode)
    {
        _areEqual = areEqual;
        _getHashCode = getHashCode;
    }

    public LambdaEqualityComparer(Func<T, T, bool> areEqual)
        : this(areEqual, obj => obj.GetHashCode())
    {
    }

    #region IEqualityComparer<T> Members

    public bool Equals(T x, T y)
    {
        return _areEqual(x, y);
    }

    public int GetHashCode(T obj)
    {
        return _getHashCode(obj);
    }

    #endregion
}

このように使えます

var comparer = new LambdaEqualityComparer<Foo>(
    (x, y) => x.Dept == y.Dept && x.Course == y.Course,
    obj => {
        unchecked {
            return 527 + obj.Dept.GetHashCode() * 31 + obj.Course.GetHashCode();
        }
    }
);

var result = fooList.Distinct(comparer);

注:内部クラスDistinctを使用し、内部Set<T>クラスがハッシュコードを使用するため、ハッシュコードの計算を提供する必要があります。


更新#2

さらに一般的な等式比較子は、比較を自動的に実装し、プロパティアクセサーのリストを受け入れます。ただし、比較の実行方法を制御することはできません。

public class AutoEqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, object>[] _propertyAccessors;

    public AutoEqualityComparer(params Func<T, object>[] propertyAccessors)
    {
        _propertyAccessors = propertyAccessors;
    }

    #region IEqualityComparer<T> Members

    public bool Equals(T x, T y)
    {
        foreach (var getProp in _propertyAccessors) {
            if (!getProp(x).Equals(getProp(y))) {
                return false;
            }
        }
        return true;
    }

    public int GetHashCode(T obj)
    {
        unchecked {
            int hash = 17;
            foreach (var getProp in _propertyAccessors) {
                hash = hash * 31 + getProp(obj).GetHashCode();
            }
            return hash;
        }
    }

    #endregion
}

使用法

var comparer = new AutoEqualityComparer<Foo>(foo => foo.Dept,
                                             foo => foo.Course);
var result = fooList.Distinct(comparer);
于 2012-04-17T14:00:50.833 に答える