简体   繁体   English

使用LINQ从两个列表中获取所有可能的连接对

[英]Using LINQ get all possible pair of connections from two lists

Given lists Input = {A, B} and Output = {1, 2, 3, 4 }, I want to get a new list that contains all possible pair of connections {connection1,connection2} : 给定列表Input = {A,B}和Output = {1,2,3,4},我想得到一个包含所有可能的连接对{connection1,connection2}的新列表:

connections = {A1, B2}, 
         {A1, B3}, 
         {A1, B4}, 
         {A2, B1},
         {A2, B3}, 
         {A2, B4}, 
         {A3, B1},
         {A3, B2},
         {A3, B4}, 
         {A4, B1},
         {A4, B2},
         {A4, B3}

Rules : 规则

  • Each combination represents a couple of 2 connections. 每个组合代表两个连接。
  • Each connection is represented by 1 input and 1 output. 每个连接由1个输入和1个输出表示。 (for example, AB is not possible) (例如,AB不可能)
  • If an input or output element is already used from the previous connection then it can't be used in the next one. 如果已从先前连接使用输入或输出元素,则不能在下一个连接中使用它。 (for example, {A1,B1} is not possible) (例如,{A1,B1}是不可能的)
  • Input can contain n elements (n>=2) and Output can contain m elements (m>=2) but the combination is always represented by only 2 connections. 输入可以包含n个元素(n> = 2),输出可以包含m个元素(m> = 2),但组合始终仅由2个连接表示。

Illustration of connection {A1, B3} 连接图示{A1,B3}

在此输入图像描述

Suggestions? 建议?

Updated Answer 更新的答案

This should do it: 这应该这样做:

using System.Linq;
using static System.Console;

class Program {
    static void Main(string[] args) {
        var inputs = new[] { "A", "B", "C" };
        var outputs = new[] { "1", "2", "3", "4" };

        var res = from i1 in inputs
                  from i2 in inputs
                  where i1 != i2
                  from o1 in outputs
                  from o2 in outputs
                  where o1 != o2
                  let c1 = i1 + o1
                  let c2 = i2 + o2
                  // Avoid {x,y} and {y,x} in result.
                  where c1.CompareTo(c2) < 0
                  select (first: c1, second: c2);

        foreach (var r in res) {
            WriteLine($"{{{r.first}, {r.second}}}");
        }
    }
}

Original Answer 原始答案

You need the LINQ to Objects equivalent of a cross join, which is just looping over the contents of both lists without any conditions to limit the set of results. 您需要相当于交叉连接的LINQ to Objects,它只是循环遍历两个列表的内容,而没有任何限制结果集的条件。

var allPairs = (from a in ListA
                from b in ListB
                select (a, b)
               ).ToList();

Will give you a list of all pairs as tuples. 将为您提供所有对的列表作为元组。

In your case you seem to want all pairs of pairs: given all combinations of input and output then get all pairs of combinations on input and output. 在你的情况下,你似乎想要所有成对的对:给定输入和输出的所有组合,然后获得输入和输出的所有组合。

Which is just a case of expanding the above with a second combination of the list of all input-output combinations. 这只是通过所有输入 - 输出组合列表的第二组合来扩展上述情况的情况。

// Assume `Input` and `Output` and enumerables of string
var inputOutputPairs = (from ip in Input
                        from op in Output
                        select ip + op
                       ).ToList();

var result = (from left in inputOutputPairs
              from right in inputOutputPairs
              select (left, right)
              // To avoid duplicates like ("A4","A4") include this:
              // where left != right
             ).ToList();

And the result will be a list of ValueTuple<string, string> . 结果将是ValueTuple<string, string>

Richard's updated answer is elegant and probably the best fit for your needs, but I suggest an alternative idea using combinatorics. 理查德的最新答案很优雅,可能最适合您的需求,但我建议使用组合学的另一种想法。 (and also using function-style linq which is imho a lot easier to debug and maintain). (还使用函数式linq,它更易于调试和维护)。

The idea is: 这个想法是:

  1. get all valid input combinations (of length 2) 获得所有有效的输入组合(长度为2)
  2. get all valid output variations (of length 2) 获得所有有效的输出变化(长度为2)
  3. combine all valid inputs with all output variations. 将所有有效输入与所有输出变化相结合。

Example implementation using a pre-baked combinatorics package from NuGet : 使用NuGet的预烘焙组合包的示例实现:

var Input = new[] { "A", "B"};
var Output = new[] { "1", "2", "3", "4" };
int maxConnections = 2;

var validInputs = new Combinations<String>(Input, maxConnections);
var validOutputs = new Variations<String>(Output, maxConnections);
var connectionsets = validInputs
    .SelectMany(ins => validOutputs
        .Select(outs => new { Ins = ins, Outs = outs })
    );

To get the connection from the format of ins/outs to single string, you could use something along the lines of : 要获得从ins / out格式到单个字符串的连接,您可以使用以下内容:

String.Join(",", set.Ins.Select((input, i) => input + set.Outs.Skip(i).First()));

NB! NB! Also note that this approach enables you to solve a bit wider question of finding N connections instead of just 2. 另请注意,此方法使您能够解决更广泛的问题,即找到N个连接,而不仅仅是2个。

I've written an unit test with the example you provide and a working implementation: 我用你提供的例子和一个有效的实现编写了一个单元测试:

public static class PairsOfConnections
{
    public static IEnumerable<Tuple<string, string>> GetAllPairsOfConnections(string[] input, string[] output)
    {
        var connectionsFromFirstInput = output.Select(o => new { Input = input[0], Output = o });
        var connectionsFromSecondInput = output.Select(o => new { Input = input[1], Output = o }).ToList();

        return from a in connectionsFromFirstInput
               from b in connectionsFromSecondInput
               where a.Output != b.Output
               select new Tuple<string, string>(a.Input + a.Output, b.Input + b.Output);
    }
}

public class PairsOfConnectionsTests
{
    [Test]
    public void TestGetAllPairsOfConnections()
    {
        string[] input = { "A", "B" };
        string[] output = { "1", "2", "3", "4" };

        IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);

        var expected = new List<Tuple<string, string>>
        {
            new Tuple<string, string>("A1","B2"),
            new Tuple<string, string>("A1","B3"),
            new Tuple<string, string>("A1","B4"),
            new Tuple<string, string>("A2","B1"),
            new Tuple<string, string>("A2","B3"),
            new Tuple<string, string>("A2","B4"),
            new Tuple<string, string>("A3","B1"),
            new Tuple<string, string>("A3","B2"),
            new Tuple<string, string>("A3","B4"),
            new Tuple<string, string>("A4","B1"),
            new Tuple<string, string>("A4","B2"),
            new Tuple<string, string>("A4","B3")
        };
        CollectionAssert.AreEquivalent(expected, result);
    }
}

Seeing that you have clarified that there can be more than two inputs, I've written a modified algorithm, with the same unit test as before, and a new one: 看到你已经澄清了可以有两个以上的输入,我编写了一个修改过的算法,使用与以前相同的单元测试,并且新的算法:

public static class PairsOfConnections
{
    public static IEnumerable<Tuple<string, string>> GetAllPairsOfConnections(string[] inputs, string[] outputs)
    {
        var connectionsFromFirstInput = outputs.Select(o => new { Input = inputs[0], Output = o }).ToList();

        var result = new List<Tuple<string, string>>();
        foreach (string input in inputs.Skip(1))
        {
            var connectionsFromNextInput = outputs.Select(output => new { Input = input, Output = output }).ToList();
            IEnumerable<Tuple<string, string>> pairs = from a in connectionsFromFirstInput
                        from b in connectionsFromNextInput
                        where a.Output != b.Output
                        select new Tuple<string, string>(a.Input + a.Output, b.Input + b.Output);

            result.AddRange(pairs);
        }

        return result;
    }
}

public class PairsOfConnectionsTests
{
    [Test]
    public void TestGetAllPairsOfConnections_WithTwoInputs()
    {
        string[] input = { "A", "B" };
        string[] output = { "1", "2", "3", "4" };

        IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);

        var expected = new List<Tuple<string, string>>
        {
            new Tuple<string, string>("A1","B2"),
            new Tuple<string, string>("A1","B3"),
            new Tuple<string, string>("A1","B4"),
            new Tuple<string, string>("A2","B1"),
            new Tuple<string, string>("A2","B3"),
            new Tuple<string, string>("A2","B4"),
            new Tuple<string, string>("A3","B1"),
            new Tuple<string, string>("A3","B2"),
            new Tuple<string, string>("A3","B4"),
            new Tuple<string, string>("A4","B1"),
            new Tuple<string, string>("A4","B2"),
            new Tuple<string, string>("A4","B3")
        };
        CollectionAssert.AreEquivalent(expected, result);
    }

    [Test]
    public void TestGetAllPairsOfConnections_WithThreeInputs()
    {
        string[] input = { "A", "B", "C" };
        string[] output = { "1", "2", "3" };

        IEnumerable<Tuple<string, string>> result = PairsOfConnections.GetAllPairsOfConnections(input, output);

        var expected = new List<Tuple<string, string>>
        {
            new Tuple<string, string>("A1","B2"),
            new Tuple<string, string>("A1","B3"),
            new Tuple<string, string>("A1","C2"),
            new Tuple<string, string>("A1","C3"),
            new Tuple<string, string>("A2","B1"),
            new Tuple<string, string>("A2","B3"),
            new Tuple<string, string>("A2","C1"),
            new Tuple<string, string>("A2","C3"),
            new Tuple<string, string>("A3","B1"),
            new Tuple<string, string>("A3","B2"),
            new Tuple<string, string>("A3","C1"),
            new Tuple<string, string>("A3","C2"),
        };
        CollectionAssert.AreEquivalent(expected, result);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM