LINQ GroupBy коллекция - PullRequest
       7

LINQ GroupBy коллекция

7 голосов
/ 15 ноября 2011

Возможно ли GroupBy в LINQ, используя свойство коллекции?

, например

void Main()
{
    var t1 = new Test() { Children = new List<string>() { "one", "two" } };
    var t2 = new Test() { Children = new List<string>() { "one", "two" } };
    var t3 = new Test() { Children = new List<string>() { "one", "three" }        };

    var tests = new List<Test>() { t1, t2, t3 };
    var anon =  from t in tests
                select new
                {
                    Children = t.Children
                };

    anon.GroupBy(t => t.Children).Dump();
}

public class Test
{
    public List<string> Children {get;set;}
}

В этом примере я надеюсь на две группы:

Key: List () {"one", "two"} Значение: t1, t2

Key: List () {"one", "three"} Значение: t3

Насколько я понимаю, анонимные типы сравниваются не по ссылке, а путем сравнения равенства их открытых свойств.

Однако фактический результат состоит из трех групп:

Key: List () {"one", "two"} Значение: t1

Key: List () {"one", "two"} Значение: t2

Key: List () {"one", "three"} Значение: t3

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

Надеюсь, объяснил это ясно ...

Ответы [ 4 ]

4 голосов
/ 15 ноября 2011

По умолчанию GroupBy будет использовать равенство ссылок при группировании по спискам (которые являются ссылочными типами).

Поскольку у вас каждый раз появляются новые экземпляры списка, они не равны.

Однако существует перегрузка из GroupBy, которая позволяет указать пользовательский IEqualityComparer , так что вы можете реализовать собственный способ сравнения списка строкНапример,

Чтобы реализовать это, существует множество других потоков о сравнении двух списков.

2 голосов
/ 15 ноября 2011

Причина, по которой вы получаете 3 группы, заключается в том, что List<T> реализует равенство с равенством ссылок по умолчанию, а не рассматривает "равенство последовательностей" содержащихся элементов между любыми двумя списками.Если вам нужна такая семантика, вам придется самостоятельно реализовать IEqualityComparer<IList<T>> (или аналогичный) и вставить его в запрос GroupBy, используя перегрузку, которая принимает средство сравнения равенства.Вот реализация sample (для массивов, а не списков, но легко адаптируемая).

Если вам удобно установить равенство (порядок и дубликаты не имеют значения),вам повезло: вы можете напрямую использовать HashSet<T> и предоставленный метод CreateSetComparer для реализации компаратора:

  var t1 = new Test { Children = new HashSet<string> { "one", "two" } };
  var t2 = new Test { Children = new HashSet<string> { "one", "two" } };
  var t3 = new Test { Children = new HashSet<string> { "one", "three" } };

  var tests = new List<Test> { t1, t2, t3 };

  // Only two groups: { one, two } and { one, three }
  tests.GroupBy(t => t.Children, HashSet<string>.CreateSetComparer())
       .Dump();
0 голосов
/ 15 ноября 2011

Я не думаю, что есть встроенный метод для этого.

Посмотрите на ответ Джона Скита здесь:

Есть ли шанс получить уникальные записи с помощью Linq (C #)?

0 голосов
/ 15 ноября 2011

Проблема в том, что списки не совсем идентичны.Он сравнивает равенство для группировки, и у вас есть два новых List<string>, которые не совсем равны.Однако вы можете объединить строки с помощью хеш-кода, который даст правильный результат:

tests.GroupBy(t => String.Join(string.Empty, t.Children.Select(c => c.GetHashCode().ToString())));
...