2009-06-14 5 views
6

clase String contiene un método muy útil - String.Join(string, string[]).Un análogo de String.Join (cadena, cadena []) para IEnumerable <T>

Crea una cadena a partir de una matriz, separando cada elemento de la matriz con un símbolo dado. Pero, en general, ¡no agrega un separador después del último elemento! Lo uso para la codificación ASP.NET para separar con "<br />" o Environment.NewLine.

Por lo tanto, quiero agregar una fila vacía después de cada fila en asp:Table. ¿Qué método de puedo usar para la misma funcionalidad?

+0

Sólo un nuevo TableRow de los valores insertados? – spender

+0

¿Cómo funciona 'IEnumerable ' con el método de unión que toma cadenas, en otras palabras, cómo está llamando al método existente? No entiendo exactamente lo que estás buscando aquí. –

+1

Actualmente no llamo 'String.Join' en absoluto. Simplemente buscando la misma funcionalidad - insertando el separador entre los elementos de la matriz – abatishchev

Respuesta

8

escribí un método de extensión:

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

Usted puede usarlo de la siguiente manera:

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

A menos que malinterprete groseramente la pregunta, el código anterior no tiene sentido. ¿Devuelve un separador * en el lugar * del último elemento? ¿Qué? –

+0

Parece que mr. @abatischev alteró mi código. Hmmm. Compruebe la revolución 1. Grrr. – spender

+0

Acabo de rediseñar las sangrías un poco, por lo que pude recordar =) lo siento si se molesta, retroceder sin duda si creo que es necesario – abatishchev

0

No hay un método integrado para hacerlo, debe hacer las suyas.

+1

"¿Qué método de IEnumerable ...?" Esta es la respuesta correcta, aunque no tan útil. Seguramente no vale la pena un voto negativo. – spender

1

Si no puedo encontrar un método que se adapte a mis necesidades, solo crearía el mío. Y los métodos de extensión son muy buenos de esa manera, ya que te permiten extender cosas así. No sé mucho acerca de ASP: mesa, pero aquí es un método de extensión al menos que se puede ajustar a cualquier cosa: p

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

Si va a hacer este tipo de cosas con frecuencia, entonces vale la pena construir su propio método de extensión para hacerlo. La implementación a continuación le permite hacer el equivalente a string.Join(", ", arrayOfStrings) donde arrayOfStrings puede ser IEnumerable<T>, y el separador puede ser cualquier objeto. Se le permite hacer algo como esto:

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

Dos cosas que me gusta de esto son:

  1. Es muy fácil de leer en un contexto de LINQ.
  2. Es bastante eficiente porque usa StringBuilder y evita evaluar la enumeración dos veces.
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

Pero evalúa la colección dos veces. Si se trata de una consulta de base de datos, entonces no es eficiente en absoluto. Además, sería más natural perder los tipos genéricos aquí y hacerlo funcionar solo con cadenas. –

+0

@Lasse Sí, ese fue un buen punto. Estaba asumiendo en memoria solamente. Ahora refactorizado para que la enumeración se evalúe solo una vez. –

+0

¿Por qué no usas una declaración 'foreach'? En este momento tiene una posible fuga de recursos. Al menos debe usar una instrucción 'using' con' .GetEnumerator() '. –

5

En .NET 3.5 se puede utilizar este método de extensión:

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

o en .NET 4

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

pero la pregunta quería un separador después de cada elemento incluyendo el último para el cual esto (versión 3.5) funcionaría: -

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

También puede usar .Agregar para hacer esto sin un método de extensión.

8

El equivalente Linq de String.Join es Aggregate

Por ejemplo:

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

Si se les da un IE de TableRows, el código será similar.

+3

Advertencia: esto podría ser funcionalmente equivalente a 'String.Join', pero la implementación es diferente. 'String.Join' es lo suficientemente inteligente como para usar un' StringBuilder' para evitar la asignación de un montón de instancias 'String' transitorias, mientras que el código anterior no lo hace. –

+0

@KentBoogaart Sin embargo, eso no es particularmente importante, ya que la pregunta original era acerca de una forma genérica de replicar el comportamiento, 'string.Join' era solo un ejemplo de una función que tiene ese comportamiento 'intercalado'. – Pharap

1

Lo que estás buscando es una función de Intersperse. Para implementaciones de LINQ de dicha función, consulte this question.


Por cierto, otra posible analógica de String.Join es la función Intercalate, que es en realidad lo que estaba buscando:

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