2009-06-14 9 views
6

La classe String contient une méthode très utile - String.Join(string, string[]).Un analogue de String.Join (string, string []) pour IEnumerable <T>

Il crée une chaîne à partir d'un tableau, en séparant chaque élément de tableau par un symbole donné. Mais général - il n'ajoute pas de séparateur après le dernier élément! Je l'utilise pour le codage ASP.NET pour séparer avec "<br />" ou Environment.NewLine. Donc je veux ajouter une ligne vide après chaque ligne dans asp:Table. Quelle méthode de puis-je utiliser pour la même fonctionnalité?

+0

Juste une nouvelle TableRow pour les valeurs insérées? – spender

+0

Comment 'IEnumerable ' fonctionne-t-il avec la méthode de jointure qui accepte les chaînes, en d'autres termes, comment appelez-vous réellement la méthode existante? Je ne comprends pas exactement ce que vous cherchez ici. –

+1

Actuellement, je n'appelle pas 'String.Join'. Juste à la recherche de la même fonctionnalité - insertion séparateur entre les éléments du tableau – abatishchev

Répondre

8

j'ai écrit une méthode d'extension:

public static IEnumerable<T> 
     Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) 
    { 
     var srcArr = src.ToArray(); 
     for (int i = 0; i < srcArr.Length; i++) 
     { 
      yield return srcArr[i]; 
      if(i<srcArr.Length-1) 
      { 
       yield return separatorFactory(); 
      } 
     } 
    } 

Vous pouvez l'utiliser comme suit:

tableRowList.Join(()=>new TableRow()) 
+2

À moins que je ne comprenne mal la question, le code ci-dessus n'a aucun sens. Il renvoie un séparateur * en place * du dernier élément ?? Quelle? –

+0

Il semble que mr. @abatischev a modifié mon code. Hmmm. Vérifiez rev 1. Grrr. – spender

+0

Je viens de repenser les retraits un peu, autant que je me souvienne =) désolé si dérangé, reculer sans aucun doute si je pense que c'est nécessaire – abatishchev

0

Il n'y a pas de méthode intégrée pour cela, vous devriez rouler les vôtres.

+1

"Quelle méthode de IEnumerable ...?" C'est la bonne réponse, mais pas si utile. Sûrement pas la peine d'une downvote. – spender

1

Si je ne trouvais pas une méthode qui répond à mes besoins, je créerais simplement la mienne. Et les méthodes d'extension sont très sympas, puisqu'elles vous permettent d'étendre les choses comme ça. Je ne sais pas beaucoup sur asp: table, mais voici une méthode d'extension au moins que vous pouvez modifier à tout: p

public static class TableRowExtensions 
{ 
    public string JoinRows(this IEnumerable<TableRow> rows, string separator) 
    { 
     // do what you gotta do 
    } 
} 
0

Si vous allez faire ce genre de chose souvent, il vaut la peine de construire votre propre méthode d'extension pour le faire. L'implémentation ci-dessous vous permet de faire l'équivalent de string.Join(", ", arrayOfStrings) où arrayOfStrings peut être un IEnumerable<T>, et le séparateur peut être n'importe quel objet du tout. Il vous permet de faire quelque chose comme ceci:

var names = new [] { "Fred", "Barney", "Wilma", "Betty" }; 
var list = names 
    .Where(n => n.Contains("e")) 
    .Join(", "); 

Deux choses que j'aime ce sont:

  1. Il est très lisible dans un contexte LINQ.
  2. C'est assez efficace car il utilise StringBuilder et évite d'évaluer l'énumération deux fois.
public static string Join<TItem,TSep>(
    this IEnumerable<TItem> enuml, 
    TSep     separator) 
{ 
    if (null == enuml) return string.Empty; 

    var sb = new StringBuilder(); 

    using (var enumr = enuml.GetEnumerator()) 
    { 
     if (null != enumr && enumr.MoveNext()) 
     { 
      sb.Append(enumr.Current); 
      while (enumr.MoveNext()) 
      { 
       sb.Append(separator).Append(enumr.Current); 
      } 
     } 
    } 

    return sb.ToString(); 
} 
+0

Mais il évalue la collection deux fois. Si c'est une requête de base de données alors ce n'est pas efficace du tout. De plus, il serait plus naturel de perdre les types génériques ici et de les faire fonctionner avec des chaînes seulement. –

+0

@Lasse Oui, c'était un bon point. Je supposais en mémoire seulement. Maintenant refactorisé pour que l'énumération ne soit évaluée qu'une seule fois. –

+0

Pourquoi n'utilisez-vous pas une instruction 'foreach'? En ce moment vous avez une fuite possible de ressource. Vous devriez au moins utiliser une instruction 'using' avec' .GetEnumerator() '. –

5

Dans .NET 3.5, vous pouvez utiliser cette méthode d'extension:

public static string Join<TItem>(this IEnumerable<TItem> enumerable, string separator) 
{ 
    return string.Join(separator, enumerable.Select(x => x.ToString()).ToArray()); 
} 

ou .NET 4

public static string Join<TItem>(this IEnumerable<TItem> enumerable, string separator) 
{ 
    return string.Join(separator, enumerable); 
} 

mais la question voulait un séparateur après chaque élément, y compris la dernier pour lequel cette (version 3.5) fonctionnerait: -

public static string AddDelimiterAfter<TItem>(this IEnumerable<TItem> enumerable, string delimiter) 
{ 
    return string.Join("", enumerable.Select(x => x.ToString() + separator).ToArray()); 
} 

Vous pouvez également utiliser .Aggregate pour ce faire sans méthode d'extension.

8

L'équivalent de Linq String.Join est Aggregate

Par exemple:

IEnumerable<string> strings; 
string joinedString = strings.Aggregate((total,next) => total + ", " + next); 

Si on leur donne un IE de tablerows, le code sera similaire.

+3

Attention: cela peut être équivalent fonctionnellement à 'String.Join', mais l'implémentation diffère. 'String.Join' est assez intelligent pour utiliser un' StringBuilder' pour éviter d'allouer tout un tas d'occurrences 'String' transitoires, alors que le code ci-dessus ne le fait pas. –

+0

@KentBoogaart Ce n'est pas particulièrement important, puisque la question initiale portait sur une façon générique de répliquer le comportement, 'string.Join' était juste un exemple d'une fonction qui a ce comportement 'intersperse'. – Pharap

1

Ce que vous cherchez est une Intersperse fonction. Pour les implémentations LINQ d'une telle fonction, voir this question.


Soit dit en passant, un autre analogue possible de String.Join est la fonction Intercalate, qui est en fait ce que je cherchais:

public static IEnumerable<T> Intercalate<T>(this IEnumerable<IEnumerable<T>> source, 
              IEnumerable<T> separator) { 
    if (source == null) throw new ArgumentNullException("source"); 
    if (separator == null) throw new ArgumentNullException("separator"); 
    return source.Intersperse(separator) 
     .Aggregate(Enumerable.Empty<T>(), Enumerable.Concat); 
} 
Questions connexes