问题
I am using LINQ:
List<String> listA = new List<string>{"a", "b", "c", "d", "e", "f", "g"};
List<String> listB = new List<string>{"1", "2", "3"};
Desired result:
{"a", "1", "b", "2", "c", "3", "d", "1", "e", "2", "f", "3", "g", "1"}
I tried but fail:
var mix = ListA.Zip(ListB, (l1, l2) => new[] { l1, l2 }).SelectMany(x => x);
//Result : {"a", "1", "b", "2", "c", "3"}
var mix = ListA.Zip(ListB, (a, b) => new[] { a, b })
.SelectMany(x => x)
.Concat(ListA.Count() < ListB.Count() ? ListB.Skip(ListA.Count()) : ListA.Skip(ListB.Count()))
.ToList();
//Result : {"a", "1", "b", "2", "c", "3", "d", "e", "f", "g"}
How can I do this using LINQ?
回答1:
This works, even if I am not sure why you need it as linq expression:
var mix = Enumerable
.Range(0, Math.Max(listA.Count, listB.Count))
.Select(i => new[] { listA[i % listA.Count], listB[i % listB.Count] })
.SelectMany(x => x);
回答2:
And how about implement your own wersion of Zip?
using System;
using System.Collections.Generic;
using System.Linq;
namespace SO
{
internal class Program
{
public static void Main(string[] args)
{
List<String> listA = new List<string> {"a", "b", "c", "d", "e", "f", "g"};
List<String> listB = new List<string> {"1", "2", "3"};
var mix = listA.ZipNew(listB, (l1, l2) => new[] {l1, l2}).SelectMany(x => x);
foreach (var m in mix)
{
Console.WriteLine(m);
}
}
}
public static class Impl
{
public static int A(this int a)
{
return 1;
}
public static IEnumerable<TResult> ZipNew<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector)
{
using (IEnumerator<TFirst> iterator1 = first.GetEnumerator())
using (IEnumerator<TSecond> iterator2 = second.GetEnumerator())
{
var i1 = true;
var i2 = true;
var i1Shorter = false;
var i2Shorter = false;
var firstRun = true;
while(true)
{
i1 = iterator1.MoveNext();
i2 = iterator2.MoveNext();
if (!i1 && (i1Shorter || firstRun))
{
iterator1.Reset();
i1 = iterator1.MoveNext();
i1Shorter = true;
firstRun = false;
}
if (!i2 && (i2Shorter || firstRun))
{
iterator2.Reset();
i2 = iterator2.MoveNext();
i2Shorter = true;
firstRun = false;
}
if (!(i1 && i2))
{
break;
}
yield return resultSelector(iterator1.Current, iterator2.Current);
}
}
}
}
}
回答3:
There is only version that tries to do the right thing and uses the right approach so far and it's the one by BWA. This can be simplified a little bit to this extension so I'll leave it here for reference:
public static IEnumerable<TResult> ZipLoop<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector)
{
if (first is null) throw new ArgumentNullException(nameof(first));
if (second is null) throw new ArgumentNullException(nameof(second));
if (resultSelector is null) throw new ArgumentNullException(nameof(resultSelector));
var (firstCycle, secondCycle) = (false, false);
var e1 = first.GetEnumerator();
var e2 = second.GetEnumerator();
try
{
while (true)
{
if (!TryMoveNextOrLoop(first, ref e1, ref firstCycle)) yield break;
if (!TryMoveNextOrLoop(second, ref e2, ref secondCycle)) yield break;
if (firstCycle && secondCycle) yield break;
yield return resultSelector(e1.Current, e2.Current);
}
}
finally
{
e1?.Dispose();
e2?.Dispose();
}
}
private static bool TryMoveNextOrLoop<T>(IEnumerable<T> source, ref IEnumerator<T> enumerator, ref bool cycle)
{
if (!enumerator.MoveNext())
{
cycle = true;
enumerator.Dispose();
enumerator = source.GetEnumerator();
return enumerator.MoveNext();
}
return true;
}
回答4:
I would go with the for
loop approach
List<String> listA = new List<string> { "a", "b", "c", "d", "e", "f", "g" };
List<String> listB = new List<string> { "1", "2", "3" };
List<string> listC = new List<string>();
for (int i = 0; i < Math.Max(listA.Count, listB.Count); i++)
{
listC.Add(listA[i % listA.Count]);
listC.Add(listB[i % listB.Count]);
}
回答5:
How about this:
listA.SelectMany((a, i) => new[] { a, listB[i % listB.Count]})
回答6:
You were very close to the (an) answer. How about this?
var listA = new List<string> { "a", "b", "c", "d", "e", "f", "g" };
var listB = new List<string> { "1", "2", "3" };
// "a", "b", "c", "d", "e", "f", "g", "a", "b", "c", "d", "e", "f", "g", etc.
// This is lazily evaluated so it doesn't matter that there are
// more elements than needed here.
var repeatedA = Enumerable.Repeat(listA, listB.Count)
.SelectMany(list => list);
// "1", "2", "3", "1", "2", "3", etc. This is lazily evaluated
// so it doesn't matter that there are more elements than needed here.
var repeatedB = Enumerable.Repeat(listB, listA.Count)
.SelectMany(list => list);
var result = repeatedA.Zip(repeatedB, (a, b) => new[] { a, b })
.SelectMany(x => x)
.Take(2 * Math.Max(listA.Count, listB.Count));
// result is {"a", "1", "b", "2", "c", "3", "d", "1", "e", "2", "f", "3", "g", "1"}
回答7:
try this...
var mix =Enumerable.Range(0, new[] { listA.Count, listB.Count }.Max()).Select(i => new[] { listA[i % listA.Count], listB[i % listB.Count] }).SelectMany(x => x);
来源:https://stackoverflow.com/questions/53169146/join-two-lists-of-different-length