拆分并连接字符串数据的多个逻辑“分支”

时间:2013-02-18 21:53:55

标签: c# recursion

我知道关于排列列表的问题有几个类似的措辞问题,但它们似乎并没有真正解决我正在寻找的问题。我知道有办法做到这一点,但我画了一个空白。我有一个类似这种格式的平面文件:

Col1|Col2|Col3|Col4|Col5|Col6
a|b,c,d|e|f|g,h|i
. . .

现在的诀窍是:我想创建这些行的所有可能排列的列表,其中行中以逗号分隔的列表表示可能的值。例如,我应该能够将表示上述内容的IEnumerable<string>取为行:

IEnumerable<string> row = new string[] { "a", "b,c,d", "e", "f", "g,h", "i" };
IEnumerable<string> permutations = GetPermutations(row, delimiter: "/");

这应该生成以下字符串数据集合:

a/b/e/f/g/i
a/b/e/f/h/i
a/c/e/f/g/i
a/c/e/f/h/i
a/d/e/f/g/i
a/d/e/f/h/i

这对我而言似乎优雅地融入了一种递归方法,但显然我在星期一有一个不好的情况,我不能完全围绕如何接近它。一些帮助将不胜感激。 GetPermutations(IEnumerable<string>, string)应该是什么样的?

4 个答案:

答案 0 :(得分:1)

我不确定这是否是最优雅的方法,但它可能会让你开始。

private static IEnumerable<string> GetPermutations(IEnumerable<string> row, 
                                                    string delimiter = "|")
{
    var separator = new[] { ',' };
    var permutations = new List<string>();
    foreach (var cell in row)
    {
        var parts = cell.Split(separator);
        var perms = permutations.ToArray();
        permutations.Clear();
        foreach (var part in parts)
        {
            if (perms.Length == 0)
            {
                permutations.Add(part);
                continue;
            }
            foreach (var perm in perms)
            {
                permutations.Add(string.Concat(perm, delimiter, part));
            }
        }
    }
    return permutations;
}

当然,如果排列的顺序很重要,您可以在最后添加.OrderBy()

编辑:添加了alernative

您还可以通过在确定排列之前计算一些数字来构建字符串数组列表。

private static IEnumerable<string> GetPermutations(IEnumerable<string> row, 
                                                    string delimiter = "|")
{
    var permutationGroups = row.Select(o => o.Split(new[] { ',' })).ToArray();
    var numberOfGroups = permutationGroups.Length;
    var numberOfPermutations = 
           permutationGroups.Aggregate(1, (current, pg) => current * pg.Length);
    var permutations = new List<string[]>(numberOfPermutations);

    for (var n = 0; n < numberOfPermutations; n++)
    {
        permutations.Add(new string[numberOfGroups]);
    }

    for (var position = 0; position < numberOfGroups; position++)
    {
        var permutationGroup = permutationGroups[position];
        var numberOfCharacters = permutationGroup.Length;
        var numberOfIterations = numberOfPermutations / numberOfCharacters;
        for (var c = 0; c < numberOfCharacters; c++)
        {
            var character = permutationGroup[c];
            for (var i = 0; i < numberOfIterations; i++)
            {
                var index = c + (i * numberOfCharacters);
                permutations[index][position] = character;
            }
        }
    }

    return permutations.Select(p => string.Join(delimiter, p));
} 

答案 1 :(得分:1)

你让我“递归”。这是另一个建议:

private IEnumerable<string> GetPermutations(string[] row, string delimiter,
                                            int colIndex = 0, string[] currentPerm = null)
{
    //First-time initialization:
    if (currentPerm == null) { currentPerm = new string[row.Length]; }

    var values = row[colIndex].Split(',');
    foreach (var val in values)
    {
        //Update the current permutation with this column's next possible value..
        currentPerm[colIndex] = val;

        //..and find values for the remaining columns..
        if (colIndex < (row.Length - 1))
        {
            foreach (var perm in GetPermutations(row, delimiter, colIndex + 1, currentPerm))
            {
                yield return perm;
            }
        }
        //..unless we've reached the last column, in which case we create a complete string:
        else
        {
            yield return string.Join(delimiter, currentPerm);
        }
    }
}

答案 2 :(得分:1)

您可以使用的一种算法基本上就像计数:

  • 从每个列表中的第0项开始(00000)
  • 增加最后一个值(00001,00002等)
  • 当你无法增加一个值时,重置它并增加下一个值(00009,00010,00011等)
  • 当你无法增加任何价值时,你就完成了。

功能:

static IEnumerable<string> Permutations(
    string input,
    char separator1, char separator2,
    string delimiter)
{
    var enumerators = input.Split(separator1)
        .Select(s => s.Split(separator2).GetEnumerator()).ToArray();
    if (!enumerators.All(e => e.MoveNext())) yield break;

    while (true)
    {
        yield return String.Join(delimiter, enumerators.Select(e => e.Current));
        if (enumerators.Reverse().All(e => {
                bool finished = !e.MoveNext();
                if (finished)
                {
                    e.Reset();
                    e.MoveNext();
                }
                return finished;
            }))
            yield break;
    }
}

用法:

foreach (var perm in Permutations("a|b,c,d|e|f|g,h|i", '|', ',', "/"))
{
    Console.WriteLine(perm);
}

答案 3 :(得分:0)

我真的认为这将是一个很好的递归函数,但我最终没有这样写。最终,这是我创建的代码:

public IEnumerable<string> GetPermutations(IEnumerable<string> possibleCombos, string delimiter)
{
    var permutations = new Dictionary<int, List<string>>();
    var comboArray = possibleCombos.ToArray();
    var splitCharArr = new char[] { ',' };

    permutations[0] = new List<string>();

    permutations[0].AddRange(
        possibleCombos
        .First()
        .Split(splitCharArr)
        .Where(x => !string.IsNullOrEmpty(x.Trim()))
        .Select(x => x.Trim()));

    for (int i = 1; i < comboArray.Length; i++)
    {
        permutations[i] = new List<string>();
        foreach (var permutation in permutations[i - 1])
        {
            permutations[i].AddRange(
                comboArray[i].Split(splitCharArr)
                .Where(x => !string.IsNullOrEmpty(x.Trim()))
                .Select(x => string.Format("{0}{1}{2}", permutation, delimiter, x.Trim()))
                );
        }
    }

    return permutations[permutations.Keys.Max()];
}

...我的测试条件为我提供了我预期的输出:

IEnumerable<string> row = new string[] { "a", "b,c,d", "e", "f", "g,h", "i" };
IEnumerable<string> permutations = GetPermutations(row, delimiter: "/");
foreach(var permutation in permutations)
{
    Debug.Print(permutation);
}

这产生了以下输出:

a/b/e/f/g/i
a/b/e/f/h/i
a/c/e/f/g/i
a/c/e/f/h/i
a/d/e/f/g/i
a/d/e/f/h/i

感谢大家的建议,他们真的有助于理清我脑子里需要做的事情。我已经提出了你所有的答案。