2009-06-14 9 views
6

classe String contiene un metodo molto utile - String.Join(string, string[]).Un analogo di String.Join (stringa, stringa []) per IEnumerable <T>

Crea una stringa da una matrice, separando ogni elemento della matrice con un simbolo indicato. Ma generale - non aggiunge un separatore dopo l'ultimo elemento! Lo utilizzo per la codifica ASP.NET per la separazione con "<br />" o Environment.NewLine.

Quindi voglio aggiungere una riga vuota dopo ogni riga in asp:Table. Quale metodo di posso utilizzare per la stessa funzionalità?

+0

solo una nuova TableRow per i valori inseriti? – spender

+0

In che modo 'IEnumerable ' funziona con il metodo join che accetta stringhe, in altre parole, come si chiama effettivamente il metodo esistente? Non capisco esattamente cosa stai cercando qui. –

+1

Attualmente non chiamo 'String.Join' affatto. Solo cercando la stessa funzionalità - inserendo il separatore tra gli elementi dell'array – abatishchev

risposta

8

ho scritto un metodo di estensione:

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

è possibile utilizzarlo come segue:

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

A meno che io non fraintenda grossolanamente la domanda, il codice di cui sopra non ha senso. Restituisce un separatore * al posto * dell'ultimo elemento ?? Che cosa? –

+0

Sembra che il sig. @abatischev ha modificato il mio codice. Hmmm. Verifica rev 1. Grrr. – spender

+0

Ho appena ridisegnato un po 'i rientri, per quanto potessi ricordare =) scusa se disturbato, torna indietro senza dubbio se pensi che sia necessario – abatishchev

0

Non esiste un metodo integrato per farlo, è necessario eseguire il rollover.

+1

"Quale metodo di IEnumerable ...?" Questa è la risposta corretta, anche se non così utile. Sicuramente non vale un downvote. – spender

1

Se non riuscissi a trovare un metodo adatto alle mie esigenze, creerei solo il mio. E i metodi di estensione sono molto belli in questo modo poiché ti permettono di estendere cose del genere. Non so molto di asp: tavolo, ma qui è un metodo di estensione, almeno, che è possibile modificare a tutto ciò: p

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

Se avete intenzione di fare questo genere di cose di frequente, allora vale la pena costruire il proprio metodo di estensione per farlo. L'implementazione qui sotto ti permette di fare l'equivalente di string.Join(", ", arrayOfStrings) dove arrayOfStrings può essere un IEnumerable<T>, e il separatore può essere qualsiasi oggetto. Esso permette di fare qualcosa di simile:

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

due cose che mi piace di questo sono:

  1. E 'molto leggibile in un contesto LINQ.
  2. È abbastanza efficiente perché utilizza StringBuilder ed evita di valutare l'enumerazione due volte.
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

Ma valuta la raccolta due volte. Se quella è una query di database, allora non è affatto efficiente. Inoltre, sarebbe più naturale perdere qui i tipi generici e farlo funzionare solo con le stringhe. –

+0

@Lasse Sì, quello era un punto giusto. Stavo assumendo solo in memoria. Ora refactored in modo che l'enumerazione venga valutata solo una volta. –

+0

Perché non usi un'istruzione 'foreach'? In questo momento hai una possibile fuga di perdite. Per lo meno dovresti usare un'istruzione 'using' con' .GetEnumerator() '. –

5

In .NET 3.5 è possibile utilizzare questo metodo di estensione:

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

o in .NET 4

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

ma la domanda ha voluto un separatore dopo ogni elemento compreso il ultimo per il quale questa (versione 3.5) avrebbe funzionato: -

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

È inoltre possibile utilizzare .Aggregarsi per fare ciò senza un metodo di estensione.

8

L'equivalente di Linq String.Join è Aggregate

Ad esempio:

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

Se dato un IE di TableRows, il codice sarà simile.

+3

Attenzione: questo potrebbe essere funzionalmente equivalente a 'String.Join', ma l'implementazione è diversa. 'String.Join' è abbastanza intelligente da usare un' StringBuilder' per evitare di allocare un gruppo di transitorie 'String', mentre il codice precedente no. –

+0

@KentBoogaart Questo non è particolarmente importante, dato che la domanda originale riguardava un modo generico di replicare il comportamento, 'string.Join' era solo un esempio di una funzione che ha il comportamento 'interspesso'. – Pharap

1

Quello che stai cercando è una funzione Intersperse. Per implementazioni LINQ di tale funzione, vedere this question.


Per inciso, un altro possibile analogico String.Join è la funzione Intercalate, che in realtà è quello che cercavo:

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