Простой способ сгруппировать Bysubelement в коллекции в LINQ?

#c# #linq

#c# #linq

Вопрос:

У меня есть обычная операция GroupBy над перечислимым:

 e.GroupBy(i => i.Property)
  

Но если i.Property действительно является коллекцией, как бы я разделил коллекцию и использовал элементы списка в качестве ключей группировки?

Например, допустим, у меня есть два объекта (Z, Y), у каждого из которых есть список:

 Z: { List = { A, B, C }}
Y: { List = { B, C, D }}
  

Теперь запуск GroupBySubelement (o => o.List) не будет группироваться по самому списку, но будет выполнять итерации по списку и генерировать следующие группировки.

 {A, {Z}}
{B, {Z, Y}}
{C, {Z, Y}}
{D, {Y}
  

Возможно ли это?

Спасибо!

Ответ №1:

Вот несколько примеров кода, который достигает желаемого:

 //This is just temporary data.  Has the similar structure to what you want.
var parts = new[]
                {
                    new
                        {
                            Name = "X",
                            Property = new[] {'A', 'B', 'C'}
                        },
                    new
                        {
                            Name = "Y",
                            Property = new[] {'B', 'C', 'D'}
                        },
                    new
                        {
                            Name = "Z",
                            Property = new char[] { }
                        }
                    };

var groupedBySub = from part in parts
                   from sub in part.Property
                   group part by sub;

foreach(var group in groupedBySub)
{
    Console.WriteLine("{0} - {1}", group.Key, string.Join(", ", group.Select(x => x.Name)));
}
  

Какие результаты:

 A - X
B - X, Y
C - X, Y
D - Y
  

Вы также можете достичь этого с помощью цепочки методов:

 var groupedBySub = parts.SelectMany(part => part.Property, (part, sub) => new {part, sub}).GroupBy(t => t.sub,  t => t.part);
  

Если вы хотите записать его с пустым списком:

 var groupedBySub = from part in parts
                   from sub in part.Property.DefaultIfEmpty()
                   group part by sub;
  

Который при замене приведенного выше кода выдает результат:

 A - X
B - X, Y
C - X, Y
D - Y
  - Z
  

Комментарии:

1. Кажется, это работает; части, которые мне не хватало, были альтернативными способами использования SelectMany и предложения DefaultIfEmpty . Спасибо.

Ответ №2:

Это сделало бы:

 var combinations = e.SelectMany(i => i.List.Select(x => new { x, i }));
var groups = combinations.GroupBy(c => c.x, c => c.i);
  

Комментарии:

1. Будет ли это отражать случай, когда список пуст?

2. Но чего вы хотите в таком случае? Группа с нулевым ключом?

Ответ №3:

Часть проблемы здесь в том, что у вас нет хорошей структуры данных:

 var z = new List<T>(); // I'm using T here, so let's pretend this is in a generic method
var y = new List<T>();
// add a bunch of stuff
  

На самом деле не существует никакого алгоритма, который может дать вам то, что вы хотите, потому что переменные Z и Y на самом деле неизвестны структуре данных, только компилятору.

Но что, если бы у вас была структура данных, подобная этой:

 var allOfTheLists = new Dictionary<T, List<T>>();
  

Затем вы могли бы разбить его, используя что-то вроде этого:

 var explodedList = allOfTheLists.SelectMany((pair) => pair.Value.Select((item) => new { pair.Key, item}));
var grouping = explodedList.GroupBy((explodedItem) => explodedItem.item);