2013-02-18 12 views
7

So che ci sono un paio di domande formulate in modo simile su SO riguardo la quotazione delle permutazioni, ma non sembrano affrontare proprio quello che sto cercando. So che c'è un modo per farlo, ma sto disegnando un vuoto. Ho un file flat che assomiglia a questo formato:Dividere e unire più "rami" logici di dati di stringa

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

Ora ecco il trucco: Voglio creare un elenco di tutte le possibili permutazioni di queste righe, in cui un elenco separato da virgole nella riga rappresenta i valori possibili. Ad esempio, dovrei essere in grado di prendere un IEnumerable<string> che rappresenta quanto sopra alle righe in quanto tale:

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

Questo dovrebbe generare la seguente raccolta di dati stringa:

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 

Questo per me sembra che sarebbe si adatta elegantemente a un metodo ricorsivo, ma a quanto pare ho un brutto caso del lunedì e non riesco a capire come avvicinarsi. Qualche aiuto sarebbe molto apprezzato. Che aspetto dovrebbe avere GetPermutations(IEnumerable<string>, string)?

+0

È questo compito? –

+0

Hah no, mi sto avvicinando 40, ma grazie per averlo chiesto. No, questo è per un'applicazione pubblicitaria. Sto cercando di leggere un file di configurazione per le pagine in cui verranno pubblicati annunci specifici. –

+0

Hai qualche codice o pseudo-codice a portata di mano? Pubblicazione che può aiutare a ottenere la risposta che stai cercando di raggiungere. Posso pensare ad un paio di modi per farlo forza bruta, ma non sono stato in grado di ridefinirlo in un metodo ricorsivo (ancora). – Tim

risposta

0

ho davvero pensato che questo sarebbe un grande funzione ricorsiva, ma ho finito per non scrivere in questo modo. In definitiva, questo è il codice che ho creato:

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

...le mie condizioni di prova mi ha fornito esattamente l'output che mi aspettavo:

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

Ciò ha prodotto il seguente risultato:

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 

grazie ai suggerimenti di tutti, in realtà sono stati utili per scelta di ciò che doveva essere fatto in la mia mente. Ho svalutato tutte le tue risposte.

1

Non sono sicuro se questo è l'approccio più elegante, ma potrebbe farti iniziare.

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

Naturalmente, se l'ordine delle permutazioni è importante, è possibile aggiungere un .OrderBy() alla fine.

Edit: ha aggiunto un alernative

Si potrebbe anche costruire una lista di array di stringhe, calcolando alcuni numeri prima di determinare le permutazioni.

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

Mi hai avuto a "ricorsivo". Ecco un altro suggerimento:

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

Un algoritmo è possibile utilizzare è fondamentalmente come il conteggio:

  • Inizia con la voce 0a in ogni elenco (00000)
  • Incremento l'ultimo valore (00001, 00002, ecc .)
  • Quando non è possibile aumentare un valore, resettarlo e incrementare il successivo (00009, 00010, 00011 ecc.)
  • Quando non è possibile aumentare alcun valore, il gioco è fatto.

Funzione:

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