我目前正在尝试使用LINQ。假设我有两个相同长度的集合:
var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };
我想将这两个集合合并为一个,但是以交错的方式。因此,结果序列应为:
"1", "a", "2", "b", "3", "c"
到目前为止,我提出的是Zip
,匿名类型和SelectMany
的组合:
var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
.SelectMany( fs => new string[] { fs.F, fs.S } );
有没有人知道用LINQ实现这种交错合并的替代/简单方法?
答案 0 :(得分:29)
警告:如果枚举的长度不同,这将跳过尾随元素。如果您宁愿用空值替换填充较短的集合,请使用下面的Andrew Shepherd's answer。
您可以编写自己的Interleave
扩展方法,例如this example。
internal static IEnumerable<T> InterleaveEnumerationsOfEqualLength<T>(
this IEnumerable<T> first,
IEnumerable<T> second)
{
using (IEnumerator<T>
enumerator1 = first.GetEnumerator(),
enumerator2 = second.GetEnumerator())
{
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
yield return enumerator1.Current;
yield return enumerator2.Current;
}
}
}
答案 1 :(得分:27)
您提供的示例可以通过省略匿名类型来简化:
var result = first.Zip(second, (f, s) => new[] { f, s })
.SelectMany(f => f);
答案 2 :(得分:9)
在接受的答案中给定的实施具有不一致性:
结果序列将始终包含第一个序列的所有元素(因为外部while
循环),但如果第二个序列包含更多元素,则不会附加这些元素。
从Interleave
方法我希望结果序列包含
min(length_1, length_2) * 2)
),或length_1 + length_2
)。以下实施遵循第二种方法
请注意or-comparison中的单个|
,以避免短路评估。
public static IEnumerable<T> Interleave<T> (
this IEnumerable<T> first, IEnumerable<T> second)
{
using (var enumerator1 = first.GetEnumerator())
using (var enumerator2 = second.GetEnumerator())
{
bool firstHasMore;
bool secondHasMore;
while ((firstHasMore = enumerator1.MoveNext())
| (secondHasMore = enumerator2.MoveNext()))
{
if (firstHasMore)
yield return enumerator1.Current;
if (secondHasMore)
yield return enumerator2.Current;
}
}
}
答案 3 :(得分:4)
您可以根据索引循环并选择数组:
var result =
Enumerable.Range(0, first.Length * 2)
.Select(i => (i % 2 == 0 ? first : second)[i / 2]);
答案 4 :(得分:2)
var result = first.SelectMany( ( f, i ) => new List<string> { f, second[ i ] } );
答案 5 :(得分:0)
这是@Douglas 答案的修改版本。这允许动态数量的 IEnumerable 交错,并遍历所有元素,而不是在最短的 IEnumerable 的末尾停止。
public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] enumerables)
{
var enumerators = enumerables.Select(e => e.GetEnumerator()).ToList();
while (enumerators.Any())
{
enumerators.RemoveAll(e => {
var ended = !e.MoveNext();
if (ended) e.Dispose();
return ended;
});
foreach (var enumerator in enumerators)
yield return enumerator.Current;
}
}