16

これを効果的に複製するためにLINQを使用して複数のリストを単一のリストにマージする巧妙な方法はありますか?

public class RGB
{
    public int Red { get; set; }
    public int Green { get; set; }
    public int Blue { get; set; }
    public RGB(int red, int green, int blue) { Red = red; Green = green; Blue = blue; }
}

public void myFunction()
{
    List<int> red = new List<int> { 0x00, 0x03, 0x06, 0x08, 0x09 };
    List<int> green = new List<int> { 0x00, 0x05, 0x06, 0x07, 0x0a };
    List<int> blue = new List<int> { 0x00, 0x02, 0x03, 0x05, 0x09 };

    List<RGB> colors = new List<RGB>();

    colors.Add(new RGB(red[0], green[0], blue[0]));
    colors.Add(new RGB(red[1], green[1], blue[1]));
    colors.Add(new RGB(red[2], green[2], blue[2]));
    colors.Add(new RGB(red[3], green[3], blue[3]));
    colors.Add(new RGB(red[4], green[4], blue[4]));
}

または、リストは別々に届くので、次のように順番にマージする方が効果的です。

public class RGB
{
    public int Red { get; set; }
    public int Green { get; set; }
    public int Blue { get; set; }

    public RGB(int red, int green, int blue) { Red = red; Green = green; Blue = blue; }
}

public void myFunction()
{
    List<int> red = new List<int> { 0x00, 0x03, 0x06, 0x08, 0x09 };

    List<RGB> colors = new List<RGB>();

    colors.Add(new RGB(red[0], 0, 0));
    colors.Add(new RGB(red[1], 0, 0));
    colors.Add(new RGB(red[2], 0, 0));
    colors.Add(new RGB(red[3], 0, 0));
    colors.Add(new RGB(red[4], 0, 0));

    List<int> green = new List<int> { 0x00, 0x05, 0x06, 0x07, 0x0a };

    colors[0].Green = green[0];
    colors[1].Green = green[1];
    colors[2].Green = green[2];
    colors[3].Green = green[3];
    colors[4].Green = green[4];

    List<int> blue = new List<int> { 0x00, 0x02, 0x03, 0x05, 0x09 };

    colors[0].Blue = blue[0];
    colors[1].Blue = blue[1];
    colors[2].Blue = blue[2];
    colors[3].Blue = blue[3];
    colors[4].Blue = blue[4];
}
4

8 に答える 8

26

基本的に、3つのコレクションを圧縮しようとしています。LINQZip()メソッドのみが2つ以上の同時圧縮をサポートしている場合。しかし残念ながら、一度にサポートできるのは2つだけです。しかし、私たちはそれを機能させることができます:

var reds = new List<int> { 0x00, 0x03, 0x06, 0x08, 0x09 };
var greens = new List<int> { 0x00, 0x05, 0x06, 0x07, 0x0a };
var blues = new List<int> { 0x00, 0x02, 0x03, 0x05, 0x09 };

var colors =
    reds.Zip(greens.Zip(blues, Tuple.Create),
        (red, tuple) => new RGB(red, tuple.Item1, tuple.Item2)
    )
    .ToList();

もちろん、3つ(またはそれ以上)を実行する拡張メソッドを作成することはそれほど苦痛ではありません。

public static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    IEnumerable<TThird> third,
    Func<TFirst, TSecond, TThird, TResult> resultSelector)
{
    using (var enum1 = first.GetEnumerator())
    using (var enum2 = second.GetEnumerator())
    using (var enum3 = third.GetEnumerator())
    {
        while (enum1.MoveNext() && enum2.MoveNext() && enum3.MoveNext())
        {
            yield return resultSelector(
                enum1.Current,
                enum2.Current,
                enum3.Current);
        }
    }
}

これにより、状況がさらに改善されます。

var colors =
    reds.Zip(greens, blues,
        (red, green, blue) => new RGB(red, green, blue)
    )
    .ToList();
于 2013-02-01T04:10:39.790 に答える
15

はい-あなたはこのようにそれを行うことができます:

List<int> red = new List<int> { 0x00, 0x03, 0x06, 0x08, 0x09 };
List<int> green = new List<int> { 0x00, 0x05, 0x06, 0x07, 0x0a };
List<int> blue = new List<int> { 0x00, 0x02, 0x03, 0x05, 0x09 };

List<RGB> colors = Enumerable
    .Range(0, red.Count)
    .Select(i => new RGB(red[i], green[i], blue[i]))
    .ToList();
于 2013-02-01T04:02:14.170 に答える
4

これは、同じタイプの任意の数のシーケンスを(配列として)取得し、それらを一緒に圧縮する簡略化されたバージョンです。

public static IEnumerable<TResult> Zip<T, TResult>(this IEnumerable<T>[] sequences, Func<T[], TResult> resultSelector)
{
    var enumerators = sequences.Select(s => s.GetEnumerator()).ToArray();
    while(enumerators.All(e => e.MoveNext()))
        yield return resultSelector(enumerators.Select(e => e.Current).ToArray());
}

長所

  • 任意の数のシーケンス
  • 4行のコード
  • LINQ.Zip()メソッドの別のオーバーロード
  • チェーン.Zipする代わりにすべてのシーケンスを一度に圧縮して、毎回シーケンスを1つ追加します

短所

  • すべてのシーケンスに同じタイプが必要です(状況に問題はありません)
  • 同じリストの長さをチェックしません(必要に応じて行を追加してください)

使用法

ファスナーの色

于 2016-07-08T16:02:35.927 に答える
4

次のようにSelectManyを使用します。

List_A.Select(a => a.List_B).SelectMany(s => s).ToList();
于 2018-12-11T12:18:53.193 に答える
3
var colours = red.Select((t, i) => new RGB(t, green[i], blue[i])).ToList();
于 2013-02-01T04:04:38.907 に答える
1

Aggregate with Zipを使用して、任意の数のIEnumerableを一度に圧縮できます。

これがあなたの例でそれをする方法です:

var colorLists = new List<int>[] { red, green, blue };
var rgbCount = red.Count;
var emptyTriples =
    Enumerable.Repeat<Func<List<int>>>(() => new List<int>(), rgbCount)
    .Select(makeList => makeList());

var rgbTriples = colorLists.Aggregate(
    emptyTriples,
    (partialTriples, channelValues) =>
        partialTriples.Zip(
            channelValues,
            (partialTriple, channelValue) =>
            {
                partialTriple.Add(channelValue);
                return partialTriple;
            }));

var rgbObjects = rgbTriples.Select(
    triple => new RGB(triple[0], triple[1], triple[2]));

一般に、基になるコンバイナーとしてZipに依存することで、入力長の変化による問題を回避できます。

于 2014-03-19T21:58:58.117 に答える
0

価値があるので、私はLINQが好きで、頻繁に使用しますが、昔ながらの方法が最適な場合もあります。これらの例に注意してください。

        const int Max = 100000;
        var rnd = new Random();
        var list1 = Enumerable.Range(1, Max).Select(r => rnd.Next(Max)).ToList();
        var list2 = Enumerable.Range(1, Max).Select(r => rnd.Next(Max)).ToList();

        DateTime start;

        start = DateTime.Now;
        var r1 = list1.Zip(list2, (a, b) => new { a, b }).ToList();
        var time1 = DateTime.Now - start;

        start = DateTime.Now;
        var r2 = list1.Select((l1, i) => new { a = l1, b = list2[i]}).ToList();
        var time2 = DateTime.Now - start;

        start = DateTime.Now;
        var r3 = new int[0].Select(i => new { a = 0, b = 0 }).ToList();
        //  Easy out-of-bounds prevention not offered in solution #2 (if list2 has fewer items)
        int max = Math.Max(list1.Count, list2.Count);
        for (int i = 0; i < max; i++)
            r3.Add(new { a = list1[i], b = list2[i] });
        var time3 = DateTime.Now - start;

        Debug.WriteLine("r1 == r2: {0}", r1.SequenceEqual(r2));
        Debug.WriteLine("r1 == r3: {0}", r1.SequenceEqual(r3));
        Debug.WriteLine("time1 {0}", time1);
        Debug.WriteLine("time2 {0}", time2);
        Debug.WriteLine("time3 {0}", time3);

出力は次のとおりです。

r1 == r2:真
r1 == r3:真
time1 00:00:00.0100071
time2 00:00:00.0170138 time3
00:00:00.0040028

もちろん、この場合(人間の知覚では)時間はほとんど目立たないので、好みになりますが、#3がはるかに速いことを知っているので、タイプがより複雑な重要なパフォーマンス領域で使用する傾向がありますまたは、列挙可能なものが大きくなる可能性があります。

また、3を使用する場合の違いにも注意してください。

        const int Max = 100000;
        var rnd = new Random();
        var list1 = Enumerable.Range(1, Max).Select(r => rnd.Next(Max)).ToList();
        var list2 = Enumerable.Range(1, Max).Select(r => rnd.Next(Max)).ToList();
        var list3 = Enumerable.Range(1, Max).Select(r => rnd.Next(Max)).ToList();

        DateTime start;

        start = DateTime.Now;
        var r1 = list1.Zip(list2, (a, b) => new { a, b }).Zip(list3, (ab, c) => new { ab.a, ab.b, c }).ToList();
        var time1 = DateTime.Now - start;

        start = DateTime.Now;
        var r2 = list1.Select((l1, i) => new { a = l1, b = list2[i], c = list3[i] }).ToList();
        var time2 = DateTime.Now - start;

        start = DateTime.Now;
        var r3 = new int[0].Select(i => new { a = 0, b = 0, c = 0 }).ToList();
        //  Easy out-of-bounds prevention not offered in solution #2 (if list2 or list3 have fewer items)
        int max = new int[] { list1.Count, list2.Count, list3.Count }.Max();
        for (int i = 0; i < max; i++)
            r3.Add(new { a = list1[i], b = list2[i], c = list3[i] });
        var time3 = DateTime.Now - start;

        Debug.WriteLine("r1 == r2: {0}", r1.SequenceEqual(r2));
        Debug.WriteLine("r1 == r3: {0}", r1.SequenceEqual(r3));
        Debug.WriteLine("time1 {0}", time1);
        Debug.WriteLine("time2 {0}", time2);
        Debug.WriteLine("time3 {0}", time3);

出力:

r1 == r2:真
r1 == r3:真
time1 00:00:00.0280393
time2 00:00:00.0089870 time3
00:00:00.0050041

予想どおり、.zipメソッドは複数の反復を実行する必要があり、最も遅くなります。

于 2014-10-11T19:25:46.353 に答える
0

Jeff Mercadoは、3つのシーケンスがzip形式で圧縮されている場合の回答を提供します。これは、すべてのシーケンスが同じアイテムタイプである必要があるという制限付きで、任意の数のシーケンスに一般化できます。

さまざまな入力長を処理し、適切なエラー処理と列挙子の適切な廃棄を行う一般化されたzip演算子を次に示します。

static class EnumerableExtensions {

  public static IEnumerable<TResult> Zip<TSource, TResult>(
    this IEnumerable<IEnumerable<TSource>> source,
    Func<IEnumerable<TSource>, TResult> resultSelector
  ) {
    if (source == null)
      throw new ArgumentNullException("source");
    if (resultSelector == null)
      throw new ArgumentNullException("resultSelector");

    var enumerators = new List<IEnumerator<TSource>>();
    try {
      foreach (var enumerable in source) {
        if (enumerable == null)
          throw new ArgumentNullException();
        enumerators.Add(enumerable.GetEnumerator());
      }

      while (enumerators.Aggregate(true, (moveNext, enumerator) => moveNext && enumerator.MoveNext()))
        yield return resultSelector(enumerators.Select(enumerator => enumerator.Current));
    }
    finally {
      foreach (var enumerator in enumerators)
        enumerator.Dispose();
    }
  }

}

次に、この一般化されたzip演算子を使用して色を計算できます。

var reds = new[] { 0x00, 0x03, 0x06, 0x08, 0x09 };
var greens = new[] { 0x00, 0x05, 0x06, 0x07, 0x0a };
var blues = new[] { 0x00, 0x02, 0x03, 0x05, 0x09 };
var colors = new[] { reds, greens, blues }
  .Zip(rgb => new RGB(rgb.First(), rgb.Skip(1).First(), rgb.Skip(2).First()));

コードは他のソリューションほど洗練されていない場合がありますが、状況によっては一般化されたzip演算子が役立つ場合があります。また、提供したコードは、各ソースシーケンスを1回だけ繰り返すため、効率的です。

于 2015-04-18T11:40:43.303 に答える