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

本文关键字:分支 数据 字符串 连接 拆分 | 更新日期: 2023-09-27 18:00:12

我知道SO上有几个措辞类似的关于排列列表的问题,但它们似乎并没有真正解决我想要的问题。我知道有办法做到这一点,但我一无所知。我有一个类似于这种格式的平面文件:

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)应该是什么样子?

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

你让我在"递归"。这里有另一个建议:

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

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

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()

编辑:添加了一个异常

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

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

您可以使用的一种算法基本上类似于计数:

  • 从每个列表中的第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);
}

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

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

多亏了每个人的建议,他们真的很有助于理清我心中需要做的事情。我对你所有的答案都投了赞成票。