Обратите внимание также, что в других униксах метод указания раздела отличается. Например, на солярии это:
man -s 1 man
EDIT: Это теперь часть MoreLINQ .
Что вам нужно, это «отличная» эффективность. Я не верю, что это часть LINQ в ее нынешнем виде, хотя ее довольно легко написать:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Итак, чтобы найти различные значения, используя только свойство Id
, вы можете использовать:
var query = people.DistinctBy(p => p.Id);
И для использования нескольких свойств вы можете использовать анонимные типы, которые соответствующим образом реализуют равенство:
var query = people.DistinctBy(p => new { p.Id, p.Name });
Untested, но он должен работать (и теперь он, по крайней мере, компилируется).
Он предполагает сопоставление по умолчанию для ключей, хотя - если вы хотите передать в компаратор равенства, просто передайте его конструктору HashSet
.
Пожалуйста, попробуйте использовать код ниже.
var Item = GetAll().GroupBy(x => x .Id).ToList();
Что делать, если я хочу получить отдельный список на основе свойств one или more ?
blockquote>Простой! Вы хотите сгруппировать их и выбрать победителя из группы.
List<Person> distinctPeople = allPeople .GroupBy(p => p.PersonId) .Select(g => g.First()) .ToList();
Если вы хотите определить группы по нескольким свойствам, вот как:
List<Person> distinctPeople = allPeople .GroupBy(p => new {p.PersonId, p.FavoriteColor} ) .Select(g => g.First()) .ToList();
Single()
, и SingleOrDefault()
каждый бросок, когда источник имеет более одного элемента. В этой операции мы ожидаем, что каждая группа может иметь более одного элемента. В этом отношении First()
предпочтительнее FirstOrDefault()
, потому что каждая группа должна иметь хотя бы один элемент .... если вы не используете EntityFramework, который не может понять, что каждая группа имеет хотя бы один член и требует FirstOrDefault()
.
– Amy B
17 July 2017 в 13:41
List<Person>lst=new List<Person>
var result1 = lst.OrderByDescending(a => a.ID).Select(a =>new Player {ID=a.ID,Name=a.Name} ).Distinct();
Вы также можете использовать синтаксис запроса, если хотите, чтобы он выглядел как LINQ-подобный:
var uniquePeople = from p in people
group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever}
into mygroup
select mygroup.FirstOrDefault();
Сначала выберите первую группу по вашим полям, затем выберите элемент firstordefault.
List<Person> distinctPeople = allPeople
.GroupBy(p => p.PersonId)
.Select(g => g.FirstOrDefault())
.ToList();
Если вы не хотите добавлять библиотеку MoreLinq в свой проект, чтобы получить функциональность DistinctBy
, вы можете получить тот же конечный результат, используя перегрузку метода Linq Distinct
, который принимает аргумент IEqualityComparer
.
Вы начинаете с создания общего пользовательского класса сравнения сравнений, который использует синтаксис лямбда для выполнения пользовательского сравнения двух экземпляров родового класса:
public class CustomEqualityComparer<T> : IEqualityComparer<T>
{
Func<T, T, bool> _comparison;
Func<T, int> _hashCodeFactory;
public CustomEqualityComparer(Func<T, T, bool> comparison, Func<T, int> hashCodeFactory)
{
_comparison = comparison;
_hashCodeFactory = hashCodeFactory;
}
public bool Equals(T x, T y)
{
return _comparison(x, y);
}
public int GetHashCode(T obj)
{
return _hashCodeFactory(obj);
}
}
Затем в вашем основном коде вы используйте его так:
Func<Person, Person, bool> areEqual = (p1, p2) => int.Equals(p1.Id, p2.Id);
Func<Person, int> getHashCode = (p) => p.Id.GetHashCode();
var query = people.Distinct(new CustomEqualityComparer<Person>(areEqual, getHashCode));
Voila! :)
Вышеприведенное предполагает следующее:
Person.Id
имеет тип int
people
не содержит любые нулевые элементы Если коллекция может содержать нули, то просто перепишите lambdas для проверки нулевого значения, например:
Func<Person, Person, bool> areEqual = (p1, p2) =>
{
return (p1 != null && p2 != null) ? int.Equals(p1.Id, p2.Id) : false;
};
EDIT
Такой подход аналогичен такому в ответе Владимира Нестеровского, но проще.
Он также похож на ответ Джоэла, но допускает сложную логику сравнения, включающую несколько свойств.
Однако, если ваши объекты могут отличаться только на Id
, другой пользователь дал правильный ответ, что все, что вам нужно сделать, это переопределить реализации по умолчанию GetHashCode()
и Equals()
в вашем Person
, а затем просто используйте готовый метод Distinct()
Linq для фильтрации любых дубликатов.
Лучший способ сделать это, который будет совместим с другими версиями .NET, - это переопределить Equals и GetHash, чтобы справиться с этим (см. вопрос о переполнении стека . Этот код возвращает различные значения. Однако, что я хочу, для возврата строго типизированной коллекции, в отличие от анонимного типа ), но если вам нужно что-то общее в вашем коде, решения в этой статье великолепны.
Вы должны иметь возможность переопределить Equals на лице, чтобы на самом деле сделать Equals on Person.id. Это должно привести к поведению, которое вы после.
Я думаю, что этого достаточно:
list.Select(s => s.MyField).Distinct();
Лично я использую следующий класс:
public class LambdaEqualityComparer<TSource, TDest> :
IEqualityComparer<TSource>
{
private Func<TSource, TDest> _selector;
public LambdaEqualityComparer(Func<TSource, TDest> selector)
{
_selector = selector;
}
public bool Equals(TSource obj, TSource other)
{
return _selector(obj).Equals(_selector(other));
}
public int GetHashCode(TSource obj)
{
return _selector(obj).GetHashCode();
}
}
Затем метод расширения:
public static IEnumerable<TSource> Distinct<TSource, TCompare>(
this IEnumerable<TSource> source, Func<TSource, TCompare> selector)
{
return source.Distinct(new LambdaEqualityComparer<TSource, TCompare>(selector));
}
Наконец, предполагаемое использование:
var dates = new List<DateTime>() { /* ... */ }
var distinctYears = dates.Distinct(date => date.Year);
Преимущество, которое я нашел с использованием этого подхода, - это повторное использование класса LambdaEqualityComparer
для других методов, которые принимают IEqualityComparer
. (О, и я оставляю материал yield
в исходной реализации LINQ ...)
Вы можете сделать это (хотя и не молниеносно) так:
people.Where(p => !people.Any(q => (p != q && p.Id == q.Id)));
То есть «выберите всех людей, в которых нет другого человека в списке с тем же идентификатором. «
Имейте в виду, что в вашем примере вы просто выберите человека 3. Я не уверен, как сообщить, что вы хотите, из предыдущих двух.
Когда мы столкнулись с такой задачей в нашем проекте, мы определили небольшой API для составления компараторов.
Итак, пример использования был таким:
var wordComparer = KeyEqualityComparer.Null<Word>().
ThenBy(item => item.Text).
ThenBy(item => item.LangID);
...
source.Select(...).Distinct(wordComparer);
И сам API выглядит следующим образом:
using System;
using System.Collections;
using System.Collections.Generic;
public static class KeyEqualityComparer
{
public static IEqualityComparer<T> Null<T>()
{
return null;
}
public static IEqualityComparer<T> EqualityComparerBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keyFunc)
{
return new KeyEqualityComparer<T, K>(keyFunc);
}
public static KeyEqualityComparer<T, K> ThenBy<T, K>(
this IEqualityComparer<T> equalityComparer,
Func<T, K> keyFunc)
{
return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer);
}
}
public struct KeyEqualityComparer<T, K>: IEqualityComparer<T>
{
public KeyEqualityComparer(
Func<T, K> keyFunc,
IEqualityComparer<T> equalityComparer = null)
{
KeyFunc = keyFunc;
EqualityComparer = equalityComparer;
}
public bool Equals(T x, T y)
{
return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) &&
EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y));
}
public int GetHashCode(T obj)
{
var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj));
if (EqualityComparer != null)
{
var hash2 = EqualityComparer.GetHashCode(obj);
hash ^= (hash2 << 5) + hash2;
}
return hash;
}
public readonly Func<T, K> KeyFunc;
public readonly IEqualityComparer<T> EqualityComparer;
}
Подробнее на нашем сайте: IEqualityComparer в LINQ .
GroupBy
, проще. Если вам это нужно в более чем одном месте, намного проще (IMO) инкапсулировать намерение. – Jon Skeet 19 February 2013 в 19:29return source.Where(e => seenKeys.Add(keySelector(e)))
. – Casey 24 July 2014 в 20:26IQueryable<T>
, я не вижу, насколько это актуально. Я согласен с тем, что это не подходит для EF и т. Д., Но в LINQ to Objects я думаю, что больше i> подходит, чемGroupBy
. Контекст вопроса всегда важен. – Jon Skeet 22 January 2017 в 18:10