【问题标题】:List<List<string>> unique permutations using LINQ [duplicate]List<List<string>> 使用 LINQ 的唯一排列 [重复]
【发布时间】:2018-11-10 22:19:58
【问题描述】:

我需要一种算法,它可以在 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);

【问题讨论】:

  • 类型推断不能在返回类型上发生,这是你最外面的Aggregate 需要从最里面的Aggregate 知道的。您可能必须明确键入您的 lambda。
  • 您将两个参数传递给内部 Aggregate - 一个 lambda 和一个 List&lt;string&gt;。这与任何signature of Aggregate 都不匹配。您不能真正从一种语言中获取函数名称和签名,并期望它们以相同的含义和顺序存在于另一种语言中。
  • @GSerg 我理解语言不能混用 - 显示 Javascript 函数的目的纯粹是为了方法。但是您关于 Aggregate 签名的观点似乎暗示这是无法做到的。如果是这样,是否有可能工作的递归函数?
  • 不,我的意思是您需要按照 C# 签名所期望的顺序传递参数。
  • @Chris 感谢您的支持; ericlippert.com/2010/06/28/… 是一个更好的链接。

标签: c# arrays permutation


【解决方案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】:

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

    短版:去我的博客吧:

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

    长版:

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

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

    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})); 
    }
    

    请注意这是如何组合三个操作的:多选查询、连接和聚合。仔细研究一下,看看它是如何工作的。

    【讨论】:

    • 哇,谢谢!你能解释一下为什么不使用 ToList() 吗?
    • @bagofmilk:因为该语言已经具有直接创建列表的语法。创建一个不是列表的集合然后从中创建一个列表是很奇怪的,因为您可以首先创建列表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多