使用Linq查找所有已过滤元素的有序组合

时间:2014-01-04 17:55:44

标签: c# linq recursion combinatorics

我希望找到所有已排序的字符串组合,仅包括具有特定字符串的字符串,给定以下结构:

//elements
string[] a = { "a1" };
string[] b = { "b1", "b2", "b3" };
string[] c = { "c1", "c2" };

//element groups
string[][] m = { a, b };
string[][] n = { a, b, c };
string[][] o = { b };
string[][] p = { b, c };

//element group groupings
string[][][] groupGroupings = { m, n, o, p };

给定groupGroupings,并过滤为仅包含组合,其中,假设“b2”存在,输出应为以下内容:

["a1", "b2"]
["a1", "b2", "c1"]
["a1", "b2", "c2"]
["b2"]
["b2", "c1"]
["b2", "c2"]

我有一个有效的递归方法,但它有点难看,我不禁觉得有更优雅的方法。

我对如何使用LINQ完成这样的事情感兴趣,不确定是否可行,但我还没有看到它。

我目前的做法:

List<IEnumerable<IEnumerable<string>>> all = new List<IEnumerable<IEnumerable<string>>>();
foreach (var groups in groupGroupings)
{
    var combos = FindAllCombinations(groups).Where(i => i.Any(g => g.Contains("b2"))).ToList();
    all.Add(combos);
}

private static IEnumerable<IEnumerable<string>> FindAllCombinations(string[][] groups)
{
    if (groups.Length > 0)
    {
        var group = groups.First();
        var remainingGroups = groups.SkipWhile(g => !g.Equals(group)).Skip(1);
        var remainingCombinations = FindAllCombinations(remainingGroups.ToArray()).ToList();

        foreach (var element in group)
        {
            if (remainingGroups.Count() > 0)
            {
                foreach (var c in remainingCombinations)
                {
                    List<string> combo = new List<string>() { element };
                    combo.AddRange(c);
                    yield return combo;
                }
            }
            else
                yield return new List<string>() { element };
        }
    }
}

1 个答案:

答案 0 :(得分:1)

我不确定你为什么不喜欢递归解决方案。我认为这是一个很好的。但是,我认为可以做一些改进。

  1. 创建ToArray()变量时调用remainingGroups。否则,将使用IEnumerable<T> / SkipWhile定义Skip,并且每次调用.Count()时,都必须一次又一次地对其进行评估,并且将枚举整个集合。使用数组,您可以使用Length代替。

  2. 我不明白你为什么要使用SkipWhilegroup始终是第一个元素,因此SkipWhile(g => !g.Equals(group))永远不会跳过任何内容。我想您可以使用Skip(1)并省略SkipWhile

  3. 您可以在外部elementList = new List<string>() { element }循环的最开始定义foreach,并使用yield return / {{1将其用于内部循环中的Concat()值方法链。

  4. ToList()

    您还可以将private static IEnumerable<IEnumerable<string>> FindAllCombinations(string[][] groups) { if (groups.Length == 0) yield break; var group = groups.First(); var remainingGroups = groups.Skip(1).ToArray(); var remainingCombinations = FindAllCombinations(remainingGroups).ToList(); foreach (var element in group) { var elementList = new List<string>() { element }; if (remainingGroups.Length > 0) { foreach (var c in remainingCombinations) { yield return elementList.Concat(c).ToList(); } } else yield return elementList; } } 更改为foreach方法:

    Select

    <强>更新

    如果您真的想使用LINQ,可以使用Eric Lippert在他的博客上描述的笛卡尔积法:Computing a Cartesian Product with LINQ

    var all = groupGroupings.Select(g => FindAllCombinations(g).Where(i => i.Any(j => j.Contains("b2"))).ToList()).ToList();