Split and join multiple logical "branches" of string data

I know there's a couple similarly worded questions on SO about permutation listing, but they don't seem to be quite addressing really what I'm looking for. I know there's a way to do this but I'm drawing a blank. I have a flat file that resembles this format:

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

Now here's the trick: I want to create a list of all possible permutations of these rows, where a comma-separated list in the row represents possible values. For example, I should be able to take an IEnumerable<string> representing the above to rows as such:

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

This should generate the following collection of string data:

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

This to me seems like it would elegantly fit into a recursive method, but apparently I have a bad case of the Mondays and I can't quite wrap my brain around how to approach it. Some help would be greatly appreciated. What should GetPermutations(IEnumerable<string>, string) look like?


You had me at "recursive". Here's another suggestion:

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

I'm not sure whether this is the most elegant approach, but it might get you started.

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

Of course, if the order of the permutations is important, you can add an .OrderBy() at the end.

Edit: added an alernative

You could also build a list of string arrays, by calculating some numbers before determining the permutations.

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

One algorithm you can use is basically like counting:

  • Start with the 0th item in each list (00000)
  • Increment the last value (00001, 00002 etc.)
  • When you can't increas one value, reset it and increment the next (00009, 00010, 00011 etc.)
  • When you can't increase any value, you're done.
  • Function:

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

    Usage:

    foreach (var perm in Permutations("a|b,c,d|e|f|g,h|i", '|', ',', "/"))
    {
        Console.WriteLine(perm);
    }
    
    链接地址: http://www.djcxy.com/p/11806.html

    上一篇: 使用Python 3中的urllib实现套接字ResourceWarning

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