使用LINQ按位置组合两个列表中的条目

时间:2014-04-22 14:12:12

标签: c# .net linq

假设我有两个包含以下条目的列表

List<int> a = new List<int> { 1, 2, 5, 10 };
List<int> b = new List<int> { 6, 20, 3 };

我想创建另一个List c,其中的条目是按两个列表中的位置插入的项目。所以List c将包含以下条目:

List<int> c = {1, 6, 2, 20, 5, 3, 10}

有没有办法在.NET中使用LINQ?我正在查看.Zip()LINQ扩展,但在这种情况下不确定如何使用它。

提前致谢!

6 个答案:

答案 0 :(得分:10)

要使用LINQ,您可以使用这段LINQPad示例代码:

void Main()
{
    List<int> a = new List<int> { 1, 2, 5, 10 };
    List<int> b = new List<int> { 6, 20, 3 };

    var result = Enumerable.Zip(a, b, (aElement, bElement) => new[] { aElement, bElement })
        .SelectMany(ab => ab)
        .Concat(a.Skip(Math.Min(a.Count, b.Count)))
        .Concat(b.Skip(Math.Min(a.Count, b.Count)));

    result.Dump();
}

输出:

LINQPad example output

这将:

  • 将两个列表压缩在一起(当元素用完时将停止)
  • 生成包含两个元素的数组(一个来自a,另一个来自b)
  • 使用SelectMany将此“展平”为一个值序列
  • 从任一列表的余数中连接(对Concat的两次调用中只有一个或两者都不应添加任何元素)

现在,说了这些,我个人会用过这个:

public static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
    using (var enumerator1 = a.GetEnumerator())
    using (var enumerator2 = b.GetEnumerator())
    {
        bool more1 = enumerator1.MoveNext();
        bool more2 = enumerator2.MoveNext();

        while (more1 && more2)
        {
            yield return enumerator1.Current;
            yield return enumerator2.Current;

            more1 = enumerator1.MoveNext();
            more2 = enumerator2.MoveNext();
        }

        while (more1)
        {
            yield return enumerator1.Current;
            more1 = enumerator1.MoveNext();
        }

        while (more2)
        {
            yield return enumerator2.Current;
            more2 = enumerator2.MoveNext();
        }
    }
}

原因:

  • 它不会多次枚举ab
  • 我对Skip
  • 的表现持怀疑态度
  • 它可以与任何IEnumerable<T>合作,而不仅仅是List<T>

答案 1 :(得分:2)

您可以尝试以下代码:

List<int> c = a.Select((i, index) => new Tuple<int, int>(i, index * 2))
               .Union(b.Select((i, index) => new Tuple<int, int>(i, index * 2 + 1)))
               .OrderBy(t => t.Second)
               .Select(t => t.First).ToList();

它使两个集合联合,然后使用索引对该联合进行排序。第一个集合中的元素具有偶数指数,来自第二个 - 奇数个。

答案 2 :(得分:2)

我创建了一个扩展方法来实现它。

public static List<T> MergeAll<T>(this List<T> first, List<T> second)
{
    int maxCount = (first.Count > second. Count) ? first.Count : second.Count;
    var ret = new List<T>();
    for (int i = 0; i < maxCount; i++)
    {
        if (first.Count < maxCount)
            ret.Add(first[i]);
        if (second.Count < maxCount)
            ret.Add(second[i]);
    }

    return ret;
}

这将迭代两个列表一次。如果一个列表比另一个列表大,它将继续添加,直到完成为止。

答案 3 :(得分:1)

为此写了一点延伸:

public static class MyEnumerable
{
    public static IEnumerable<T> Smash<T>(this IEnumerable<T> one, IEnumerable<T> two)
    {
        using (IEnumerator<T> enumeratorOne = one.GetEnumerator(), 
                              enumeratorTwo = two.GetEnumerator())
        {
            bool twoFinished = false;

            while (enumeratorOne.MoveNext())
            {
                yield return enumeratorOne.Current;

                if (!twoFinished && enumeratorTwo.MoveNext())
                {
                    yield return enumeratorTwo.Current;
                }
            }

            if (!twoFinished)
            {
                while (enumeratorTwo.MoveNext())
                {
                    yield return enumeratorTwo.Current;
                }
            }
        }
    }
}

用法:

var a = new List<int> { 1, 2, 5, 10 };
var b = new List<int> { 6, 20, 3 };

var c = a.Smash(b); // 1, 6, 2, 20, 5, 3, 10

var d = b.Smash(a); // 6, 1, 20, 2, 3, 5, 10

这适用于任何IEnumerable,因此您也可以这样做:

var a = new List<string> { "the", "brown", "jumped", "the", "lazy", "dog" };
var b = new List<string> { "quick", "dog", "over" };

var c = a.Smash(b); // the, quick, brown, fox, jumped, over, the, lazy, dog

答案 4 :(得分:1)

您可以使用Concat和您通过索引订购的匿名类型:

List<int> c = a
    .Select((val, index) => new { val, index })
    .Concat(b.Select((val, index) => new { val, index }))
    .OrderBy(x => x.index)
    .Select(x => x.val)
    .ToList();

然而,由于这不是很优雅,也不如:

c = new List<int>(a.Count + b.Count);
int max = Math.Max(a.Count, b.Count);
int aMax = a.Count;
int bMax = b.Count;
for (int i = 0; i < max; i++)
{ 
    if(i < aMax)
        c.Add(a[i]);
    if(i < bMax)
        c.Add(b[i]);
}

我根本不会使用LINQ。

答案 5 :(得分:0)

很抱歉添加第二种扩展方法,灵感来自另外两种,但我更喜欢它:

static IEnumerable<T> Intertwine<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
    using (var enumerator1 = a.GetEnumerator())
    using (var enumerator2 = b.GetEnumerator()) {
        bool more1 = true, more2 = true;
        do {
            if (more1 && (more1 = enumerator1.MoveNext()))
                yield return enumerator1.Current;
            if (more2 && (more2 = enumerator2.MoveNext()))
                yield return enumerator2.Current;
        } while (more1 || more2);
    }
}