Как получить комбинации, отличные от некоторого поля с помощью LINQ

#c# #.net #linq

#c# #.net #linq

Вопрос:

Мне нужен запрос LINQ, чтобы получить все комбинации (отличные по имени) для следующей структуры:

 var keys = new[]
{
    new { Name = "A", Value = "1" },
    new { Name = "A", Value = "2" },
    new { Name = "B", Value = "3" },
    new { Name = "B", Value = "4" },
    // etc
};
  

Мне нужно получить:

 {A1, B3} {A1, B4} {A2, B3} {A2, B4} // etc
  

где под A1-B4 я подразумеваю весь элемент: { Name = "...", Value = "..." }

Исходный массив может содержать не только элементы A и B. Например, если мы добавим элемент, то { Name = "C", Value = "5" } результат вывода элементов должен содержать 3 элемента типа {A1, B3, C5} .

Спасибо.

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

1. Может быть .. но я думаю, что с LINQ решение должно быть коротким и более элегантным.

Ответ №1:

Эта проблема состоит из нескольких этапов:

  1. Разделите список по «имени» на список списков L
  2. Выполните декартово произведение списка LxL, где списки различны
  3. Выполните декартово произведение каждой пары списков
  4. Объедините все результаты.

И вот реализация:

 var NameLists = keys.GroupBy(k => k.Name);

var NameListPairs = from first in NameLists
                    from second in NameLists where first != second
                    select new {first, second};

var Result = from pair in NameListPairs
             from first in pair.first
             from second in pair.second
             select new {first, second};
  

И вот оно. Обратите внимание на общую схему того, как выполнить декартово произведение, где мы выбираем форму двух перечислений одновременно.

Редактировать:

Если то, что вы хотите сделать, это декартово произведение во всех списках имен, тогда используйте этот фрагмент от Эрика Липперта. Как только у вас это будет, вы можете использовать это следующим образом:

 var Result = keys.GroupBy(k => k.Name).CartesianProduct();
  

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

1. Спасибо, Крис, но я хочу прояснить один момент. Исходный массив может содержать не только элементы A и B. Например, если мы добавим элемент { Name = «C», Value = «4»}. Элементы выходного результата должны содержать 3 элемента типа {A1, B3, C5}.

Ответ №2:

Попробуйте что-то вроде этого:

 var combinations = from A in keys.Where(k=>k.Name == "A")
                   from B in keys.Where(k=>k.Name == "B")
                   select new {A,B};
  

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

1. Спасибо, Кейтс, но я хочу прояснить один момент. Исходный массив может содержать не только элементы A и B. Например, если мы добавим элемент { Name = «C», Value = «4»}. Элементы выходного результата должны содержать 3 элемента, таких как {A1, B3, C5}.

2. Что ж, вы можете расширить этот запрос по мере необходимости для любого количества известных измерений. В общем случае это становится сложнее. Смотрите мою правку.

Ответ №3:

Если вы хотите использовать Linq, то посмотрите на оператор Join и взломайте в нем свой собственный компаратор.

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

         //
    // Summary:
    //     Correlates the elements of two sequences based on matching keys. A specified
    //     System.Collections.Generic.IEqualityComparer<T> is used to compare keys.
    //
    // Parameters:
    //   outer:
    //     The first sequence to join.
    //
    //   inner:
    //     The sequence to join to the first sequence.
    //
    //   outerKeySelector:
    //     A function to extract the join key from each element of the first sequence.
    //
    //   innerKeySelector:
    //     A function to extract the join key from each element of the second sequence.
    //
    //   resultSelector:
    //     A function to create a result element from two matching elements.
    //
    //   comparer:
    //     An System.Collections.Generic.IEqualityComparer<T> to hash and compare keys.
    //
    // Type parameters:
    //   TOuter:
    //     The type of the elements of the first sequence.
    //
    //   TInner:
    //     The type of the elements of the second sequence.
    //
    //   TKey:
    //     The type of the keys returned by the key selector functions.
    //
    //   TResult:
    //     The type of the result elements.
    //
    // Returns:
    //     An System.Collections.Generic.IEnumerable<T> that has elements of type TResult
    //     that are obtained by performing an inner join on two sequences.
    //
    // Exceptions:
    //   System.ArgumentNullException:
    //     outer or inner or outerKeySelector or innerKeySelector or resultSelector
    //     is null.
    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer);
  

Ответ №4:

Это приведет ко всем комбинациям, включая {B3, A1} и т.д.

 var cobinations = from a in keys
              from b in keys.Where(k => k.Name != a.Name)
              select new{ a, b };
  

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

1. Спасибо, поп, но я хочу прояснить один момент. Исходный массив может содержать не только элементы A и B. Например, если мы добавим элемент { Name = «C», Value = «4»}. Элементы выходного результата должны содержать 3 элемента, таких как {A1, B3, C5}.