2009-06-14 2 views
6

класс String содержит очень полезный метод - String.Join(string, string[]).Аналог String.Join (строка, строка []) для IEnumerable <T>

Он создает строку из массива, разделяя каждый элемент массива с заданным символом. Но вообще - он не добавляет разделителя после последнего элемента! Я использую его для кодирования ASP.NET для разделения на «<br />» или Environment.NewLine.

Так что я хочу добавить пустую строку после каждой строки в asp:Table. Какой метод я могу использовать для этой же функции?

+0

Просто новый TableRow для вставляемых значений? – spender

+0

Как работает 'IEnumerable ' с методом join, который принимает строки, другими словами, как вы на самом деле называете существующий метод? Я не совсем понимаю, что вы ищете здесь. –

+1

В настоящее время я не вызываю 'String.Join'. Просто ищет такую ​​же функциональность - вставка разделителя между элементами массива – abatishchev

ответ

8

Я написал метод расширения:

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

Вы можете использовать его следующим образом:

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

Если я грубо неправильно понимаю вопрос, приведенный выше код не имеет смысла. Он возвращает разделитель * на место * последнего элемента? Какие? –

+0

Кажется, что мр. @abatischev изменил мой код. Хммм. Проверить rev 1. Grrr. – spender

+0

Я просто переработал отступы немного, насколько я мог помнить =) Извините, если вас беспокоит, откажитесь, без сомнения, подумайте, что это необходимо – abatishchev

0

Для этого не существует встроенного метода, вы должны катиться самостоятельно.

+1

«Какой метод IEnumerable ...?» Это правильный ответ, хотя и не такой полезный. Наверняка не стоит нисходящего. – spender

1

Если бы я не мог найти способ, который мне подходит, я бы просто создал свой собственный. И методы расширения очень приятные, так как они позволяют вам расширять такие вещи. Есть много о осины не знаю: таблицы, но вот метод расширения, по крайней мере, который вы можете настроить на что угодно: р

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

Если вы собираетесь делать подобные вещи часто, то для этого нужно создать свой собственный метод расширения. Нижеприведенная реализация позволяет сделать эквивалент string.Join(", ", arrayOfStrings), где arrayOfStrings может быть IEnumerable<T>, а разделителем может быть любой объект вообще. Это позволяет сделать что-то вроде этого:

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

Две вещи, которые я люблю об этом являются:

  1. Это очень читаемый в контексте LINQ.
  2. Это довольно эффективно, потому что он использует StringBuilder и позволяет избежать повторного вычисления перечисления.
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

Но он дважды оценивает коллекцию. Если это запрос базы данных, то он неэффективен вообще. Кроме того, было бы более естественно потерять общие типы здесь и заставить работать только с строками. –

+0

@ Lasse Да, это была справедливая точка. Я предполагал только в памяти. Теперь реорганизуем так, чтобы перечисление оценивалось только один раз. –

+0

Почему вы не используете инструкцию 'foreach'? Прямо сейчас у вас есть возможная утечка. Вы, по крайней мере, должны использовать оператор 'using' с' .GetEnumerator() '. –

5

В .NET 3.5 вы можете использовать этот метод расширения:

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

или в .NET 4

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

НО вопрос хотел разделитель после каждого элемента, включая последний, для которого это (версия 3.5) будет работать: -

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

Вы также можете использовать .Агрегат для этого без дополнительного метода.

8

Linq эквивалент String.Join является Aggregate

Например:

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

Если передан IE из TableRows, код будет похож.

+3

Предупреждение: это может быть эквивалентно функционально 'String.Join', но реализация отличается. 'String.Join' достаточно умен, чтобы использовать' StringBuilder', чтобы не выделять целую кучу переходных экземпляров 'String', тогда как в приведенном выше коде нет. –

+0

@KentBoogaart Это не особенно важно, поскольку исходный вопрос касался общего способа воспроизведения поведения, 'string.Join' был всего лишь примером функции, которая имеет такое« межповерочное »поведение. – Pharap

1

Что вы ищете, является функцией Intersperse. Для реализации LINQ такой функции см. this question.


Кстати, еще один возможный аналог String.Join функция Intercalate, которая на самом деле то, что я искал:

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