30

Enumerable.Aggregate(...)メソッドを使用して、セミコロンで区切られた文字列のリストを連結したいと考えています。むしろ簡単ですね。

次の点を考慮してください。

  • private const string LISTSEPARATOR = "; ";
  • アルバム。OrderedTracksList<TrackDetails>
  • TrackDetailsにはDiscNumber Int16 がありますか? 財産

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 ステートメントでこれを行うことは可能ですか?

前もって感謝します。

4

5 に答える 5

49

文字列のリストを連結するには、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
)

このオーバーロードを使用すると、シード値を指定できます。シード値が指定されている場合、コレクションが空の場合は結果としても使用されます。

編集:本当に を使用したい場合は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()
于 2013-02-15T14:46:23.507 に答える
11

次のヘルパー拡張メソッドが役立つと思います。

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

これでも「上から下へ」と表示されるため、読みやすさが大幅に向上します。

于 2013-02-15T21:17:59.410 に答える
7

String.Join次のように使用します。

 txtDiscNumber.Text = String.Join(LISTSEPARATOR,
      album.OrderedTracks
                  .Where(a => a.DiscNumber.HasValue)
                  .Select(a => a.DiscNumber.Value.ToString())
                  .Distinct());
于 2013-02-15T14:46:58.417 に答える
0

デバッグ目的でそのようなメソッドをよく使用し、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);
于 2013-02-15T15:42:10.957 に答える