Using Enumerable.Aggregate(...) Method over an empty sequence
Solution 1
To concatenate a list of strings, use the string.Join
method.
The Aggregate
function doesn't work with empty collections. It requires a binary accumulate function and it needs an item in the collection to pass to the binary function as a seed value.
However, there is an overload of Aggregate
:
public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector
)
This overload allows you to specify a seed value. If a seed value is specified, it will also be used as the result if the collection is empty.
EDIT: If you'd really want to use Aggregate
, you can do it this way:
sequence.Aggregate(string.Empty, (x, y) => x == string.Empty ? y : x + Separator + y)
Or this way by using StringBuilder
:
sequence.Aggregate(new StringBuilder(), (sb, x) => (sb.Length == 0 ? sb : sb.Append(Separator)).Append(x)).ToString()
Solution 2
I think you might find the following helper extension method useful.
public static TOut Pipe<TIn, TOut>(this TIn _this, Func<TIn, TOut> func)
{
return func(_this);
}
It allows you to express your query in the following way.
txtDiscNumber.Text = album.OrderedTracks
.Where(a => a.DiscNumber.HasValue)
.Select(a => a.DiscNumber.Value.ToString())
.Distinct()
.Pipe(items => string.Join(LISTSEPARATOR, items));
This still reads "top to bottom," which greatly aids readability.
Solution 3
Use String.Join
like this:
txtDiscNumber.Text = String.Join(LISTSEPARATOR,
album.OrderedTracks
.Where(a => a.DiscNumber.HasValue)
.Select(a => a.DiscNumber.Value.ToString())
.Distinct());
Solution 4
You can use
.Aggregate(string.Empty, (i, j) => i + LISTSEPARATOR + j);
with the initial value it works for empty collections
lorcan
Updated on June 16, 2022Comments
-
lorcan almost 2 years
I would like to use the Enumerable.Aggregate(...) method to concatenate a list of strings separated by a semicolon. Rather easy, isn't it?
Considering the following:
private const string LISTSEPARATOR = "; ";
- album.OrderedTracks is
List<TrackDetails>
- TrackDetails has DiscNumber Int16? property
The following statement will trow an exception if the sequence returned by Distinct() is empty (as the Aggregate() method doesn't apply on empty sequence):
txtDiscNumber.Text = album.OrderedTracks .Where(a => a.DiscNumber.HasValue) .Select(a => a.DiscNumber.Value.ToString()) .Distinct() .Aggregate((i, j) => i + LISTSEPARATOR + j);
The workaround I am using:
List<string> DiscNumbers = album.OrderedTracks .Where(a => a.DiscNumber.HasValue) .Select(a => a.DiscNumber.Value.ToString()) .Distinct() .ToList(); if (!DiscNumbers.Any()) txtDiscNumber.Text = null; else txtDiscNumber.Text = DiscNumbers.Aggregate((i, j) => i + LISTSEPARATOR + j);
Is there any better solution? Is it possible to do this in a single LINQ statement?
Thanks in advance.
-
Cosmin over 7 yearsPlease note that if you use the seed value, it will appear at the begining of the result string:
;item1;item2
-
Abhishek about 5 yearsThis is the actual answer for how to use Aggregate for this. Yes Join is better in this very specific situation, but not really the question