将两个序列及其元素交织在一起

时间:2019-04-22 13:14:25

标签: c# linq

我想连接两个序列的元素,产生一个包含原始两个元素的所有序列的单个序列,但它们的元素交错。

Concat LINQ方法可以进行串联,但不需要交织,因此我需要一些特殊的东西。

交织规则如下:

  • 对于每对元素,应调用selector函数,所选元素应为第一个或第二个元素,具体取决于函数的boolean结果(true :第一,false:第二)

这是我想要实现的一个实际示例:

var sequence1 = new int[] { 1, 2, 3 };
var sequence2 = new int[] { 11, 12, 13 };
var result = sequence1.ConcatInterleaved(sequence2, (a, b) => (a + b) % 3 == 0);
Console.WriteLine(String.Join("\r\n", result));

预期输出:

1  // Because (1 + 11) % 3 == 0, the first is selected
11 // Because (2 + 11) % 3 != 0, the second is selected
12 // Because (2 + 12) % 3 != 0, the second is selected
2  // Because (2 + 13) % 3 == 0, the first is selected
13 // Because (3 + 13) % 3 != 0, the second is selected
3  // Because sequence2 has no more elements, the next element of sequence1 is selected

我想要一个LINQ解决方案,以便按照内置LINQ方法的精神推迟实际的串联。这是我目前的尝试:

public static IEnumerable<TSource> ConcatInterleaved<TSource>(
    this IEnumerable<TSource> source,
    IEnumerable<TSource> other,
    Func<TSource, TSource, bool> selector)
{
    // What to do?
}

更新:我更改了示例,因此它看起来不像是简单的交替交错。

关于selector函数的说明:该函数不适用于两个序列的预选对,就像它在Zip方法中一样。这些对不是预定义的。每次选择后,将形成一个新对,其中包含先前选择的被拒绝元素,以及先前选择的序列中的一个新元素。

例如,使用选择器(a, b) => trueConcatInterleaved等效于Concat:返回sequence1的所有元素,然后返回sequence2的所有元素。另一个示例:使用选择器(a, b) => false返回sequence2的所有元素,然后返回sequence1的所有元素。

1 个答案:

答案 0 :(得分:2)

public static IEnumerable<T> Weave(
  this IEnumerable<T> left,
  IEnumerable<T> right,
  Func<T, T, bool> chooser)
{
  using(var leftEnum = left.GetEnumerator())
  using(var rightEnum = right.GetEnumerator())
  {
    bool moreLeft = leftEnum.MoveNext;
    bool moreRight = rightEnum.MoveNext;
    while(moreLeft && moreRight)
    {
      if (chooser(leftEnum.Current, rightEnum.Current))
      {
        yield return leftEnum.Current;
        moreLeft = leftEnum.MoveNext();
      }
      else
      {
        yield return rightEnum.Current;
        moreRight = rightEnum.MoveNext();
      } 
    }
    // yield the buffered item, if any
    if (moreLeft) yield return leftEnum.Current;
    if (moreRight) yield return rightEnum.Current;

    // yield any leftover elements
    while (leftEnum.MoveNext()) yield return leftEnum.Current;
    while (rightEnum.MoveNext()) yield return rightEnum.Current;
  }
}