2010-12-09 24 views
1

Je voudrais créer une collection de chaînes (en utilisant C# et éventuellement LINQ) à partir d'une seule chaîne csv où chaque valeur est un signe plus ou moins suivi d'un caractère. Par exemple:Expansion de caractères génériques dans une chaîne csv pour générer une collection de chaînes csv?

"+A,+E,+B,-B,+C,+D" 

La chaîne peut également contenir un caractère générique représentant deux de n'importe quel caractère; une avec un signe plus, puis une avec un signe négatif. Si une chaîne contient un caractère générique, je voudrais remplacer le caractère générique et générer une liste de chaînes sans joker. Par exemple que j'ai eu:

"+A,-A,*" 

Les cordes je veux générer seraient:

+A,-A,+A,-A 
+A,-A,+B,-B 
+A,-A,+C,-C 
+A,-A,... 
+A,-A,+Z,-Z 

Et de même pour plusieurs jokers. La chaîne "*,*" produirait:

+A,-A,+A,-A 
+A,-A,+B,-B 
+A,-A,+C,-C 
+A,-A,... 
+A,-A,+Z,-Z 
+B,-B,+A,-A 
+B,-B,+B,-B 
+B,-B,+C,-C 
+B,-B,... 
+B,-B,+Z,-Z 
+C,-C,... 

Mon instinct me dit qu'il doit y avoir une solution simple élégante, mais il me éludant aujourd'hui. Des idées? Cela ressemble à un algorithme parfait pour profiter de LINQ avec? Merci de votre aide!

Répondre

2
IEnumerable<string> Wildcard = from c in Enumerable.Range(0, 26) 
           let ch = (char)('A' + c) 
           select string.Concat('+', ch, ',', '-', ch); 

IEnumerable<string> ExpandLine(string[] xs, int i) 
{ 
    var ys = (xs[i] == "*") ? Wildcard : new[] { xs[i] }; 

    if (i == xs.Length - 1) 
     return ys; 
    else 
     return from y in ys 
       from z in ExpandLine(xs, i + 1) 
       select y + "," + z; 
} 

IEnumerable<string> ExpandLines(IEnumerable<string> xs) 
{ 
    return from x in xs 
      from y in ExpandLine(x.Split(','), 0) 
      select y; 
} 

Exemple:

var result = ExpandLines(new[] { "+A,-A,*" }).ToList(); 

Résultat:

 
+A,-A,+A,-A 
+A,-A,+B,-B 
+A,-A,+C,-C 
    : 
    : 
+A,-A,+Z,-Z 

(26 articles)


Exemple 2:

var result = ExpandLines(new[] { "+A,-A,*,*" }).ToList(); 

Résultat:

 
+A,-A,+A,-A,+A,-A 
+A,-A,+A,-A,+B,-B 
+A,-A,+A,-A,+C,-C 
     : 
     : 
+A,-A,+Z,-Z,+X,-X 
+A,-A,+Z,-Z,+Y,-Y 
+A,-A,+Z,-Z,+Z,-Z 

(676 articles)

+0

Quel résultat ne 'var result = ExpandLines (nouveau [] { "+ A, -A, *, *"}) ToList().' Donner? –

+0

@El Ronnoco: Une liste avec 676 articles. – dtb

+0

Merci pour votre aide !! – Evan

1

Voici un autre:

IEnumerable<string> ExpandWildcards(IEnumerable<string> lines) 
{ 
    return lines.SelectMany(ExpandWildcards); 
} 

IEnumerable<string> ExpandWildcards(string input) 
{ 
    string[] parts = input.Split(','); 
    var expanded = parts.Select(ExpandSingleItem); 
    return expanded.CartesianProduct().Select(line => line.JoinStrings(",")); 
} 

static readonly string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 

IEnumerable<string> ExpandSingleItem(string item) 
{ 
    if (item == "*") 
     return _chars.Select(c => string.Format("+{0},-{0}", c)); 
    return new[] { item }; 
} 

static class Extensions 
{ 
    // CartesianProduct method by Eric Lippert (http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/) 
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) 
    { 
     IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
     return sequences.Aggregate( 
      emptyProduct, 
      (accumulator, sequence) => 
      from accseq in accumulator 
      from item in sequence 
      select accseq.Concat(new[] {item}));     
    } 

    public static string JoinStrings(this IEnumerable<string> strings, string separator) 
    { 
     return strings.Aggregate(
         default(StringBuilder), 
         (sb, item) => sb == null 
          ? new StringBuilder(item) 
          : sb.Append(separator).Append(item), 
         sb => sb.ToString()); 
    } 
} 

Certes, il est un peu plus longue que la solution de DTB ... Mais ce n'est pas récursif, ce qui peut être important si vous avez beaucoup d'articles par ligne. Cela donne les mêmes résultats.

+0

Merci pour votre contribution! – Evan

2

Une généralisation de votre problème est la production de chaque chaîne qui se conforme à une grammaire sans contexte particulier. J'ai écrit une série en neuf parties sur la façon de faire une telle chose en C#; cela pourrait vous intéresser si vous avez des problèmes plus complexes dans cette veine.

http://blogs.msdn.com/b/ericlippert/archive/tags/grammars/

+0

Merci pour le lien! – Evan