LINQ: Farklı değerler


137

Bir XML set aşağıdaki öğe var:

id           category

5            1
5            3
5            4
5            3
5            3

Bu öğelerin ayrı bir listesine ihtiyacım var:

5            1
5            3
5            4

LINQ'da Kategori VE Kimliği için nasıl farklı olabilirim?

Yanıtlar:


222

Birden fazla alandan farklı olmaya mı çalışıyorsunuz? Öyleyse, anonim bir tür ve Distinct operatörü kullanın ve tamam olmalıdır:

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

Eğer bir "büyük" türündeki değerler ayrı set almak için çalışıyorsanız, fakat sadece belirginliği yönü için bazı özelliklerini alt kümesi bakarak, muhtemelen istediğiniz DistinctByuygulandığı şekliyle MoreLINQ içinde 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;
         }
     }
 }

(Karşılaştırma nullaracı olarak iletirseniz, anahtar türü için varsayılan karşılaştırıcıyı kullanır.)


Oh yani "daha büyük tip" ile hala farklılığı belirlemek için sadece birkaç özellik karşılaştırmak istesem bile sonuçtaki tüm özellikleri istiyorum demek olabilir?
Bezelye

@TheRedPea: Evet, kesinlikle.
Jon Skeet


27

Jon Skeet'in cevabına ek olarak, her grup yinelemesi için w / sayım boyunca benzersiz grupları elde etmek için grubu ifadelerle de kullanabilirsiniz:

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
"Jon Skeet'in cevabına ek olarak" yazdın ... Böyle bir şeyin mümkün olup olmadığını bilmiyorum. ;)
Yehuda Makarov

13

Hala bakanlar için; özel bir lambda karşılaştırıcısı uygulamanın başka bir yolu.

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;
        }
    }

daha sonra linq Distinct için lambda'yı alabilecek bir uzantı oluşturabilirsiniz

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

Kullanımı:

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

Referans kaynağına bakıldığında, Distinct önceden vermiş olduğu öğeleri saklamak için bir karma kümesi kullanır. Her zaman aynı karma kodun döndürülmesi, daha önce döndürülen her öğenin her seferinde inceleneceği anlamına gelir. Daha sağlam bir karma kod işleri hızlandırır çünkü yalnızca aynı karma grubundaki öğelerle karşılaştırır. Sıfır, makul bir varsayılan değerdir, ancak karma kodu için ikinci bir lambda'yı desteklemeye değer olabilir.
Darryl

İyi bir nokta! Zaman aldığımda düzenlemeyi deneyeceğim, şu anda bu alanda çalışıyorsanız, düzenlemek için çekinmeyin
Ricky G

8

Cevaba biraz geç kaldım, ancak bunu sadece gruplandırmak istediğiniz değerleri değil, tüm öğeyi istiyorsanız yapmak isteyebilirsiniz:

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

Bu, DistinctBy kullanan Jon Skeets ikinci örneğinde olduğu gibi, IEqualityComparer karşılaştırıcısını uygulamadan, grubunuza seçimle eşleşen ilk tüm öğeyi verecektir. DistinctBy büyük olasılıkla daha hızlı olacaktır, ancak performans bir sorun değilse yukarıdaki çözüm daha az kod içerecektir.


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

Her öğeye tam olarak bir kez sahip olduğumuzdan bahsettiğimiz için, bir “set” benim için daha anlamlı.

Sınıflar ve uygulanan IEqualityComparer örneği:

 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;
        }
    }

şimdi

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

setList benzersiz unsurlara sahip olacak

Bunu .Except()bir set farkı döndürürken uğraşırken düşündüm

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.