LINQ: различные значения



у меня есть следующий набор элементов из XML:



id           category

5 1
5 3
5 4
5 3
5 3


мне нужен отдельный список этих элементов:



5            1
5 3
5 4


Как я могу отличить для категории и Id тоже в LINQ?

590   7  

7 ответов:

вы пытаетесь различить более чем одно поле? Если это так, просто используйте анонимный тип и отдельный оператор, и все должно быть в порядке:

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 как компаратор, он будет использовать компаратор по умолчанию для типа ключа.)

просто использовать Distinct() с вашим собственным компаратором.

http://msdn.microsoft.com/en-us/library/bb338049.aspx

в дополнение к ответу Джона Скита, вы также можете использовать выражения 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() };

для любого, кто все еще ищет; вот еще один способ реализации пользовательского лямбда-компаратора.

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

Я немного опоздал с ответом, но вы можете сделать это, если хотите, чтобы весь элемент, а не только значения, которые вы хотите сгруппировать:

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

Это даст вам первый целый элемент, соответствующий вашей группе по выбору, так же, как Джон Скитс второй пример с использованием DistinctBy, но без реализации IEqualityComparer comparer. DistinctBy, скорее всего, будет быстрее, но решение выше будет включать меньше кода, если производительность не является проблемой.

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

поскольку мы говорим о том, что каждый элемент ровно один раз, "набор" имеет для меня больше смысла.

пример с классами и 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() который возвращает set-difference

Comments

    Ничего не найдено.