LINQ:異なる値


136

次のアイテムをXMLから設定しました。

id           category

5            1
5            3
5            4
5            3
5            3

これらのアイテムの明確なリストが必要です:

5            1
5            3
5            4

LINQでもカテゴリとIDを区別するにはどうすればよいですか?

回答:


221

複数のフィールドで区別しようとしていますか?その場合は、匿名型とDistinct演算子を使用するだけで問題ありません。

var query = doc.Elements("whatever")
               .Select(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Distinct();

「より大きな」タイプの個別の値のセットを取得しようとしているが、個別性の側面のプロパティの一部のサブセットのみを参照している場合DistinctByは、MoreLINQの実装として次のようにする必要がありDistinctBy.csます。

 public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
     this IEnumerable<TSource> source,
     Func<TSource, TKey> keySelector,
     IEqualityComparer<TKey> comparer)
 {
     HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
     foreach (TSource element in source)
     {
         if (knownKeys.Add(keySelector(element)))
         {
             yield return element;
         }
     }
 }

null比較子として渡すと、キータイプのデフォルトの比較子が使用されます。)


ああ、「より大きなタイプ」とは、いくつかのプロパティを比較して区別性を判断するだけでも、結果にすべてのプロパティが必要であることを意味しますか?
Red Pea

@TheRedPea:はい、その通りです。
Jon Skeet、2016


27

Jon Skeetの回答に加えて、group by式を使用して、各グループの反復のカウントとともに一意のグループを取得することもできます。

var query = from e in doc.Elements("whatever")
            group e by new { id = e.Key, val = e.Value } into g
            select new { id = g.Key.id, val = g.Key.val, count = g.Count() };

4
あなたは「ジョン・スキートの答えに加えて」と書いた...そのようなことが可能かどうかはわかりません。;)
イェフダマカロフ

13

まだ探している人のために; これは、カスタムのラムダコンパレータを実装する別の方法です。

public class LambdaComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> _expression;

        public LambdaComparer(Func<T, T, bool> lambda)
        {
            _expression = lambda;
        }

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

        public int GetHashCode(T obj)
        {
            /*
             If you just return 0 for the hash the Equals comparer will kick in. 
             The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
             Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
             you will always fall through to the Equals check which is what we are always going for.
            */
            return 0;
        }
    }

次に、ラムダを取り込むことができるlinq Distinctの拡張機能を作成できます

   public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list,  Func<T, T, bool> lambda)
        {
            return list.Distinct(new LambdaComparer<T>(lambda));
        }  

使用法:

var availableItems = list.Distinct((p, p1) => p.Id== p1.Id);

参照ソースを見ると、Distinctはハッシュセットを使用して、既に生成した要素を格納します。常に同じハッシュコードを返すことは、以前に返されたすべての要素が毎回検査されることを意味します。同じハッシュバケット内の要素と比較するだけなので、より堅牢なハッシュコードはスピードアップします。ゼロは妥当なデフォルトですが、ハッシュコードの2番目のラムダをサポートする価値があるかもしれません。
ダリル

いい視点ね!時間があるときに編集してみます。現時点でこのドメインで作業している場合は、自由に編集してください
Ricky G

8

私は答えに少し遅れていますが、グループ化したい値だけでなく、要素全体が必要な場合は、これを行うことができます。

var query = doc.Elements("whatever")
               .GroupBy(element => new {
                             id = (int) element.Attribute("id"),
                             category = (int) element.Attribute("cat") })
               .Select(e => e.First());

これにより、Jon SkeetsのDistinctByを使用した2番目の例のように、選択によってグループに一致する最初の要素全体が得られますが、IEqualityComparer比較子は実装されていません。DistinctByの方が高速である可能性が高いですが、パフォーマンスが問題にならない場合は、上記のソリューションに含まれるコードが少なくなります。


4
// First Get DataTable as dt
// DataRowComparer Compare columns numbers in each row & data in each row

IEnumerable<DataRow> Distinct = dt.AsEnumerable().Distinct(DataRowComparer.Default);

foreach (DataRow row in Distinct)
{
    Console.WriteLine("{0,-15} {1,-15}",
        row.Field<int>(0),
        row.Field<string>(1)); 
}

0

私たちはすべての要素を1回だけ持つことについて話しているので、「セット」は私にとってより意味があります。

クラスとIEqualityComparerを実装した例:

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

        public Product(int x, string y)
        {
            Id = x;
            Name = y;
        }
    }

    public class ProductCompare : IEqualityComparer<Product>
    {
        public bool Equals(Product x, Product y)
        {  //Check whether the compared objects reference the same data.
            if (Object.ReferenceEquals(x, y)) return true;

            //Check whether any of the compared objects is null.
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                return false;

            //Check whether the products' properties are equal.
            return x.Id == y.Id && x.Name == y.Name;
        }
        public int GetHashCode(Product product)
        {
            //Check whether the object is null
            if (Object.ReferenceEquals(product, null)) return 0;

            //Get hash code for the Name field if it is not null.
            int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode();

            //Get hash code for the Code field.
            int hashProductCode = product.Id.GetHashCode();

            //Calculate the hash code for the product.
            return hashProductName ^ hashProductCode;
        }
    }

List<Product> originalList = new List<Product> {new Product(1, "ad"), new Product(1, "ad")};
var setList = new HashSet<Product>(originalList, new ProductCompare()).ToList();

setList ユニークな要素があります

.Except()セット差を返すものに対処しながら私はこれを考えました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.