C# 如何合并被拆分为两个索引为奇数和偶数的数组?
从这个问题: 我使用此方法将一个数组拆分为两个分别具有奇数和偶数索引的数组:C# 如何合并被拆分为两个索引为奇数和偶数的数组?,c#,linq,collections,C#,Linq,Collections,从这个问题: 我使用此方法将一个数组拆分为两个分别具有奇数和偶数索引的数组: int[] a = new int[] { 1, 3, 7, 8 }; int[] aEven = a.Where((x, i) => i % 2 == 0).ToArray(); int[] aOdd = a.Where((x, i) => i % 2 != 0).ToArray(); 这将产生两个阵列: aEven : {1, 7} aOdd : {3, 8} 如何以优雅的方式将aEven/a
int[] a = new int[] { 1, 3, 7, 8 };
int[] aEven = a.Where((x, i) => i % 2 == 0).ToArray();
int[] aOdd = a.Where((x, i) => i % 2 != 0).ToArray();
这将产生两个阵列:
aEven : {1, 7}
aOdd : {3, 8}
如何以优雅的方式将aEven
/aOdd
合并回原来的a
数组
注意:我没有更改aEven
/aOdd
数组,并且保证两个数组具有相同的长度
我需要从aEven
/aOdd
输入中获得的输出是:
{ 1, 3, 7, 8 };
首先,您可以使用Concat合并它们,然后,如果您希望对reslt进行排序,可以使用OrderBy:
aEven.Concat(aOdd).OrderBy(b => b).ToArray();
一种方法是结合
Enumerable.Zip
和Enumerable.Aggregate
以及一些精细化。请注意,这仍然使用引擎盖下的环
var aList = aEven.Zip(aOdd, (even, odd) => new {even, odd})
.Aggregate(new List<int>(aEven.Length + aOdd.Length),
(list, z) =>
{
list.Add(z.even);
list.Add(z.odd);
return list;
});
if (aEven.Length != aOdd.Length)
aList.Add(aEven[aEven.Length-1]);
var aOutput = aList.ToArray();
for (var i = 0; i < aOutput.Length; ++i)
Console.WriteLine($"aOutput[{i}] ==> {aOutput[i]} == {a[i]} <== a[{i}]");
另一种方法是结合使用Zip
、SelectMany
和Concat
(说明最后一项):
简单的for循环可能仍然是最简单的解决方案。使用扩展方法创建
联锁,是的,没有从循环中转义:
public static IEnumerable<T> InterlockWith<T>(this IEnumerable<T> seq1, IEnumerable<T> seq2)
{
Tuple<T[], int>[] metaSequences = new Tuple<T[], int>[2];
metaSequences[0] = Tuple.Create(seq1.ToArray(), seq1.Count());
metaSequences[1] = Tuple.Create(seq2.ToArray(), seq2.Count());
var orderedMetas = metaSequences.OrderBy(x => x.Item2).ToArray();
for (int i = 0; i < orderedMetas[0].Item2; i++)
{
yield return metaSequences[0].Item1[i];
yield return metaSequences[1].Item1[i];
}
var remainingItems = orderedMetas[1].Item1.Skip(orderedMetas[0].Item2);
foreach (var item in remainingItems)
{
yield return item;
}
}
只需对
循环使用。它不会创建所有这些元组的中间数组,但可读性相当,并且需要相同数量的代码
int[] Merge(int[] evenItems, int[] oddItems)
{
var itemsCount = evenItems.Length + oddItems.Length;
var result = new int[itemsCount];
for (var i = 0; i < itemsCount; i++)
{
var sourceIndex = Math.DivRem(i, 2, out var remainder);
var source = remainder == 0 ? evenItems : oddItems;
result[i] = source[sourceIndex];
}
return result;
}
int[]合并(int[]evenItems,int[]oddItems)
{
var itemscont=evenItems.Length+oddItems.Length;
var结果=新整数[itemsCount];
对于(变量i=0;i
这种方法遇到了回答者的一些批评
- 它很优雅
- 它解决了合并长度可能非常不同的数组的一般情况
- 它只枚举一次O(N+M)
- 它不分配需要垃圾收集的中间数组对象
- 理论上应该更快,但我还没有测量性能
它不是C。它是C#-完全不同的语言。它只是一个错误。Sorry任何解决方案都将涉及一个循环,无论是直接(即for
)还是间接(即LINQ的zip
)是否要更新原始数组?似乎您从未修改过它,所以它已经是正确的了。或者,您的意思是要合并两个数组,以创建一个与原始数组相同的新数组(这样,您可能根本没有原始数组,并且正在尝试重建它)?希望在我编辑后,投票关闭的每个人都知道我的问题:)它不会保持与a
相同的顺序,除非a
的顺序已知(在示例中它似乎是升序),否则这个问题无法解决。否则,合并两个数组不能保证恢复原始顺序,因为不能保证它在奇偶之间交替。。。事实上,在本例中并没有这样做。您必须将发布的代码分配给一个变量,如var result=aEven.Concat(aOdd).OrderBy(b=>b.ToArray()代码>因为它不会更改OP中声明的任何变量question@SlavenToji谢谢,你是对的,我想这是很清楚的,但正如你所做的那样,最好提一下。如果你能保证它的长度总是相等的,那么不,你不知道。它只适用于这种情况,试着运行以下命令:Merge(new int[]{1,2,5,10},新int[]{8,11})
@MohamedElshawaf当初始数组被拆分为两个元素,元素保留在偶数和奇数索引上时,您无法获得这样的输入。此代码是另一种方式(evens
应首先开始),但在我做了修改后,效果非常好。很好。哦,呵呵。好的,我做了改变。谢谢作为旁注,如果计数大于偶数,它将从赔率
中跳过一项,但对于OP问题效果很好,我喜欢您使用这些LINQ方法组合的方式though@MohamedElshawaf对于一般问题,只需调用Concat两次。Skip
不是数组或列表的“专用”(如ElementAt
是,例如与List
或其他几个一起)这意味着Skip
调用必须迭代数组的所有项(可能根本不产生任何项),然后可能调用两次??如果你问我的话,一般情况下不太好。
public static IEnumerable<T> InterlockWith<T>(this IEnumerable<T> seq1, IEnumerable<T> seq2)
{
Tuple<T[], int>[] metaSequences = new Tuple<T[], int>[2];
metaSequences[0] = Tuple.Create(seq1.ToArray(), seq1.Count());
metaSequences[1] = Tuple.Create(seq2.ToArray(), seq2.Count());
var orderedMetas = metaSequences.OrderBy(x => x.Item2).ToArray();
for (int i = 0; i < orderedMetas[0].Item2; i++)
{
yield return metaSequences[0].Item1[i];
yield return metaSequences[1].Item1[i];
}
var remainingItems = orderedMetas[1].Item1.Skip(orderedMetas[0].Item2);
foreach (var item in remainingItems)
{
yield return item;
}
}
a = aEven.InterlockWith(aOdd).ToArray();
int[] Merge(int[] evenItems, int[] oddItems)
{
var itemsCount = evenItems.Length + oddItems.Length;
var result = new int[itemsCount];
for (var i = 0; i < itemsCount; i++)
{
var sourceIndex = Math.DivRem(i, 2, out var remainder);
var source = remainder == 0 ? evenItems : oddItems;
result[i] = source[sourceIndex];
}
return result;
}
var merged = evens
.Zip(odds, (even, odd) => new [] {even, odd})
.SelectMany(pair => pair)
.Concat(evens.Skip(odds.Length))
.ToArray();
public static IEnumerable<T> MergeByZipAndRemainder<T>(T[] this firsts, T[] seconds)
{
int maxLength = Math.Max(firsts.Length, seconds.Length);
foreach(int i in Enumerable.Range(0, maxLength))
{
if (i < firsts.Length) { yield return firsts[i]; }
if (i < seconds.Length) { yield return seconds[i]; }
}
}
var result = evens.MergeByZipAndRemainder(odds).ToArray();