Enumerable.Aggregate(...)メソッドを使用して、セミコロンで区切られた文字列のリストを連結したいと思います。なかなか簡単ですね。
次のことを考慮してください。
private const string LISTSEPARATOR = "; ";
List<TrackDetails>
次のステートメントは、Distinct()によって返されるシーケンスが空の場合(Aggregate()として)、例外をスローします。 メソッドは空のシーケンスには適用されません):
txtDiscNumber.Text = album.OrderedTracks
.Where(a => a.DiscNumber.HasValue)
.Select(a => a.DiscNumber.Value.ToString())
.Distinct()
.Aggregate((i, j) => i + LISTSEPARATOR + j);
私が使用している回避策:
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);
より良い解決策はありますか?単一のLINQステートメントでこれを行うことは可能ですか?
前もって感謝します。
文字列のリストを連結するには、 string.Join
メソッド。
Aggregate
関数は、空のコレクションでは機能しません。バイナリ累積関数が必要であり、シード値としてバイナリ関数に渡すためにコレクション内のアイテムが必要です。
ただし、 Aggregate
の過負荷 があります:
public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector
)
このオーバーロードにより、シード値を指定できます。シード値が指定されている場合、コレクションが空の場合は結果としても使用されます。
EDIT:本当にAggregate
を使用したい場合は、次のように実行できます。
sequence.Aggregate(string.Empty, (x, y) => x == string.Empty ? y : x + Separator + y)
または、このようにStringBuilder
を使用します。
sequence.Aggregate(new StringBuilder(), (sb, x) => (sb.Length == 0 ? sb : sb.Append(Separator)).Append(x)).ToString()
次のヘルパー拡張方法が役立つと思います。
public static TOut Pipe<TIn, TOut>(this TIn _this, Func<TIn, TOut> func)
{
return func(_this);
}
これにより、クエリを次のように表現できます。
txtDiscNumber.Text = album.OrderedTracks
.Where(a => a.DiscNumber.HasValue)
.Select(a => a.DiscNumber.Value.ToString())
.Distinct()
.Pipe(items => string.Join(LISTSEPARATOR, items));
これはまだ「上から下」と表示され、読みやすさを大幅に向上させます。
使用する String.Join
このような:
txtDiscNumber.Text = String.Join(LISTSEPARATOR,
album.OrderedTracks
.Where(a => a.DiscNumber.HasValue)
.Select(a => a.DiscNumber.Value.ToString())
.Distinct());
あなたが使用することができます
.Aggregate(string.Empty, (i, j) => i + LISTSEPARATOR + j);
初期値を使用すると、空のコレクションで機能します
デバッグの目的でそのような多くの方法を使用して、2つの拡張メソッドを考え出しました。
public static string Concatenate<T, U>(this IEnumerable<T> source, Func<T, U> selector, string separator = ", ")
{
if (source == null)
{
return string.Empty;
}
return source
.Select(selector)
.Concatenate(separator);
}
public static string Concatenate<T>(this IEnumerable<T> source, string separator = ", ")
{
if (source == null)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder();
bool firstPass = true;
foreach (string item in source.Distinct().Select(x => x.ToString()))
{
if (firstPass)
{
firstPass = false;
}
else
{
sb.Append(separator);
}
sb.Append(item);
}
return sb.ToString();
}
このように使用します:
string myLine = myCol.Concatenate(x => x.TheProperty);