Сообщите LINQ Distinct, какой товар вернуть - PullRequest
3 голосов
/ 30 марта 2010

Я понимаю, как сделать Distinct () для IEnumerable и что мне нужно создать IEqualityComparer для более сложных вещей, однако есть ли способ, с помощью которого вы можете сказать, какой дублированный элемент нужно вернуть?

Например, скажем, у вас есть List<T>

List<MyClass> test = new List<MyClass>();
test.Add(new MyClass {ID = 1, InnerID = 4});
test.Add(new MyClass {ID = 2, InnerID = 4});
test.Add(new MyClass {ID = 3, InnerID = 14});
test.Add(new MyClass {ID = 4, InnerID = 14});

Затем вы делаете:

var distinctItems = test.Distinct(new DistinctItemComparer());

class DistinctItemComparer : IEqualityComparer<MyClass> {

    public bool Equals(MyClass x, MyClass y) {
        return x.InnerID  == y.InnerID;;
    }

    public int GetHashCode(MyClassobj) {
        return obj.InnerID.GetHasCode();
    }
}

Этот код вернет классы с идентификаторами 1 и 3. Есть ли способ вернуть идентификаторы совпадений 2 и 4.

Ответы [ 5 ]

4 голосов
/ 30 марта 2010

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

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

items.OrderByDescending(x => x.Id)
     .Distinct(new DistinctItemComparer());

Обратите внимание, что одной из альтернатив использования Distinct с пользовательским компаратором является использование DistinctBy из MoreLINQ :

items.OrderByDescending(x => x.Id)
     .DistinctBy(x => x.InnerId);

Хотя вы не можете гарантировать, что обычный порядок LINQ to Objects от Distinct не изменится, я был бы рад добавить гарантию к MoreLINQ :) (Это единственный порядок, который в любом случае имеет смысл честно.)

Еще одной альтернативой было бы использовать GroupBy вместо этого - тогда для каждого внутреннего идентификатора вы можете получить все соответствующих предметов и перейти оттуда.

3 голосов
/ 30 марта 2010

Тогда вы не хотите различаться - вы хотите сгруппировать ваши элементы и выбрать для них «максимальный» элемент на основе идентификатора:

    var distinctItems = test.Distinct(new DistinctItemComparer());

    var otherItems = test.GroupBy(a => a.InnerID, (innerID, values) => values.OrderBy(b => b.ID).Last());

    var l1 = distinctItems.ToList();
    var l2 = otherItems.ToList();

l1 = ваш текущий список l2 = ваш желаемый список

2 голосов
/ 30 марта 2010

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

 var q = from t in tests
         group t by t.InnerID into g
         select g.First(...);

В предложении select вы получите набор элементов, которые равны , и вы можете выбрать один конкретный элемент, который вам нужен (например, с помощью First(...)). На самом деле вам не нужно добавлять Distinct в конец, потому что вы уже выбираете только один элемент для каждой из групп.

2 голосов
/ 30 марта 2010

Это не похоже на работу для Distinct, это звучит как работа для Where. Вы хотите фильтровать последовательность в вашем случае:

var ids = new[] { 2, 4 };
var newSeq = test.Where(m => ids.Contains(m.ID));
1 голос
/ 30 марта 2010

Нет, нет пути.

Distinct() используется для поиска различных элементов. Если вы беспокоитесь о том, какой элемент вернуть ... тогда очевидно, что он не является действительно идентичным (и, следовательно, не отличным), и у вас есть недостаток в вашем дизайне.

...