列表<列表<字符串>&GT;使用LINQ

时间:2018-05-31 20:17:53

标签: c# arrays permutation

我需要一种算法,它可以在List中使用任意数量的List并生成一组唯一的排列。我更喜欢找到LINQ解决方案。

我实际上有一个运行良好的Javascript函数,我正在尝试用C#重新创建它(参见底部的代码)

C#(我的尝试) - Visual Studio不喜欢我的第二个Aggregate()。它说the arguments cannot be inferred from usage

public static void testit()
{
    List<List<string>> master = new List<List<string>>();

    List<string> voltages = new string[] { "208", "230", "460" }.ToList();
    List<string> sysConfigs = new string[] { "10205", "10210", "10215", "10220" }.ToList();

    master.Add(voltages);
    master.Add(sysConfigs);

    var amp = master.Aggregate(
            (a, b) => a.Aggregate(
                (r, v) => r.Concat(
                    b.Select(w => new List<string>().Concat(v, w))
                ), new List<string>()
            )
    ); 
}

这个新集合的输出应如下所示:

/*
   OUTPUT (displayed as arrays - but will be lists):
   [
     ["208", "10205"],
     ["208", "10210"],
     ["208", "10215"],
     ["208", "10220"],
     ["230", "10205"],
     ["230", "10210"],
     ["230", "10215"],
     ["230", "10220"],
     ["460", "10205"],
     ["460", "10210"],
     ["460", "10215"],
     ["460", "10220"]
   ];

这是一个很好用的Javascript函数,我试图模仿C#:

function getPermutations(arr) {
   return arr.reduce(
       (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), [])
   );

}

var voltages = ["208", "230", "460"];
var sysConfigs = ["10205", "10210", "10215", "10220"];

var master = [];
master.push(voltages);
master.push(sysConfigs);

var newArr = getPermutations(master);
console.log(newArr);

3 个答案:

答案 0 :(得分:7)

正如其他问题所述,这是笛卡尔积,而非排列。

简短版:请访问我的博客:

https://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/

长版:

两个列表的笛卡尔积以C#中的SelectMany形式或查询理解为内置。让我们从那开始。

在我们开始讨论之前,请不要这样做:

List<string> voltages = new string[] { "208", "230", "460" }.ToList()

要么这样做:

IEnumerable<string> voltages = new string[] { "208", "230", "460" };

或者这样做:

List<string> voltages = new List<string>() { "208", "230", "460" };

但是不要制作一个数组,然后列出它!只需从头开始列表。

好的,继续。我们有两个序列:

IEnumerable<string> voltages = new string[] { "208", "230", "460" };
IEnumerable<string> sysConfigs = new string[] { "10205", "10210", "10215", "10220" };

我们想要他们的笛卡儿产品:

IEnumerable<IEnumerable<string>> master = 
  from v in voltages
  from s in sysConfigs
  select new List<string>() { v, s };

我们已经完成了。

如果您不喜欢“理解形式”,那么您可以使用“流利的形式”:

IEnumerable<IEnumerable<string>> master = 
  voltages.SelectMany(
    v => sysConfigs, 
    (s, v) => new List<string>() { v, s });

如果您需要列表清单:

List<List<string>> master = 
  voltages.SelectMany(
    v => sysConfigs, 
    (v, s) => new List<string>() { v, s })
  .ToList();

轻松自负。

此操作的含义应该清楚,但如果不是:一般形式是:

var zs = 
  from x in xs
  from y in f(x) // f takes x and returns a collection of ys
  g(x, y)        // do something with every combination of x and y to make a z

在你的情况下,f(x)只是“总是产生第二个集合”,但它不一定是;该集合可能取决于x。结果是z的序列。

现在,您需要的是任意多个序列的笛卡尔积。

你直觉认为这是连接的集合是正确的。我们可以这样解决:

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
  this IEnumerable<IEnumerable<T>> sequences) 
{
  IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
  return sequences.Aggregate( 
    emptyProduct, 
    (accumulator, sequence) => 
      from accseq in accumulator 
      from item in sequence 
      select accseq.Concat(new[] {item})); 
}

请注意这是如何组合这三个操作的:select-many查询,串联和聚合。仔细研究,看看它是如何工作的。

答案 1 :(得分:1)

这是您的代码的正确版本

var amp = master.Aggregate(
    new List<List<string>>(){new List<string>()},
    (a, b) => a.Aggregate(
        new List<List<string>>(new List<List<string>>()),
        (r, v) => r.Concat(
            b.Select(w => v.Concat(new List<string>{w}).ToList())
        ).ToList()));

答案 2 :(得分:-1)

这些不是排列(需要一个列表)。

您的输出显示两组的笛卡尔积。

这会带你到MoreLinq.Caresian(listA, listB)
MoreLinq是一个Nuget包。

代码希望

 var amps = master[0]
     .Cartesian(master[1], (a, b) => new List<string> { a, b } );

所以你们应该放弃master并直接使用电压和sysConfigs这样做。