views:

591

answers:

8

class String contains very useful method - String.Join(string, string[]).

It creates a string from an array, separating each element of array with a symbol given. But general - it doesn't add a separator after the last element! I uses it for ASP.NET coding for separating with "<br />" or Environment.NewLine.

So I want to add an empty row after each row in asp:Table. What method of IEnumerable<TableRow> can I use for the same functionality?

A: 

There is no built in method to do that, you should roll your own.

driis
"What method of IEnumerable<TableRow> ...?" This is the correct answer, albeit not that helpful. Surely not worth a downvote.
spender
+3  A: 

This other stackoverflow question is very similar to yours.

ichiban
+1  A: 

If I couldn't find a method that suits my need, I would just create my own. And extension methods are very nice that way since they let you extend stuff like that. Don't know much about asp:table, but here is an extension method at least which you can tweak to whatever :p

public static class TableRowExtensions
{
    public string JoinRows(this IEnumerable<TableRow> rows, string separator)
    {
        // do what you gotta do
    }
}
Svish
+7  A: 

I wrote an extension method:

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

You can use it as follows:

tableRowList.Join(()=>new TableRow())
spender
A: 

If you are going to do this sort of thing frequently, then it's worth building your own extension method to do it. The implementation below allows you to do the equivalent of string.Join(", ", arrayOfStrings) where the arrayOfStrings can be an IEnumerable<T>, and separator can be any object at all. It allows you to do something like this:

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

Two things I like about this are:

  1. It's very readable in a LINQ context.
  2. It's fairly efficient because it uses StringBuilder and avoids evaluating the enumeration twice.
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();
}
Damian Powell
But it evaluates the collection twice. If that is a database query then it isn't efficient at all. Additionally, it would be more natural to lose the generic types here and make it work with strings only.
Lasse V. Karlsen
@Lasse Yes, that was a fair point. I was assuming in-memory only. Now refactored so that the enumeration is evaluated only once.
Damian Powell
Why don't you use a `foreach` statement? Right now you have a possible resouce leak. You at the very least should use a `using` statement with the `.GetEnumerator()`.
Matthew Whited
@Matthew Yes, well spotted, I missed the using statement. I can't use foreach though because that causes the enumeration to be evaluated twice (see the history of this answer for an example!)
Damian Powell
+1  A: 

In .NET 3.5 you can use this extension method:

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

or in .NET 4

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

BUT the question wanted a separator after each element including the last for which this (3.5 version) would work:-

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

You could also use .Aggregate to do this without an extension method.

Hightechrider
+3  A: 

The Linq equivalent of String.Join is Aggregate

For instance:

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

If given an IE of TableRows, the code will be similar.

Scott Weinstein