如何一次迭代两个数组?

我在解析文本文件时构建了两个数组。 第一个包含列名,第二个包含当前行的值。 我需要一次迭代两个列表来构建一个地图。 现在我有以下内容:

var currentValues = currentRow.Split(separatorChar); var valueEnumerator = currentValues.GetEnumerator(); foreach (String column in columnList) { valueEnumerator.MoveNext(); valueMap.Add(column, (String)valueEnumerator.Current); } 

这很好用,但它并不能完全满足我的优雅感,如果arrays的数量大于2,它会变得非常毛茸茸(我偶尔会这样做)。 有没有人有另一个更高级的成语?

如果列名与每行中的元素数相同,是否可以不使用for循环?

 var currentValues = currentRow.Split(separatorChar); for(var i=0;i 

你的初始代码中有一个非显而易见的伪bug – IEnumerator扩展了IDisposable所以你应该处理它。 这对于迭代器块来说非常重要! 对于数组来说不是问题,但是与其他IEnumerable实现一样。

我这样做:

 public static IEnumerable PairUp (this IEnumerable source, IEnumerable secondSequence, Func projection) { using (IEnumerator secondIter = secondSequence.GetEnumerator()) { foreach (TFirst first in source) { if (!secondIter.MoveNext()) { throw new ArgumentException ("First sequence longer than second"); } yield return projection(first, secondIter.Current); } if (secondIter.MoveNext()) { throw new ArgumentException ("Second sequence longer than first"); } } } 

然后,只要有需要,您就可以重复使用:

 foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar), (column, value) => new { column, value }) { // Do something } 

或者,您可以创建一个通用的Pair类型,并取消PairUp方法中的投影参数。

编辑:

使用Pair类型,调用代码如下所示:

 foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar)) { // column = pair.First, value = pair.Second } 

这看起来就像你能得到的一样简单。 是的,您需要将实用程序方法放在某处,作为可重用的代码。 在我看来,这几乎不成问题。 现在有多个arrays……

如果数组的类型不同,我们就会遇到问题。 你不能在generics方法/类型声明中表达任意数量的类型参数 – 你可以根据需要为尽可能多的类型参数编写PairUp的版本,就像最多4个委托参数的ActionFunc委托一样 – 但是你不能随心所欲。

但是,如果这些值都属于同一类型 – 并且如果您乐于坚持使用数组 – 这很容易。 (非arrays也没关系,但你不能提前做长度检查。)你可以这样做:

 public static IEnumerable Zip(params T[][] sources) { // (Insert error checking code here for null or empty sources parameter) int length = sources[0].Length; if (!sources.All(array => array.Length == length)) { throw new ArgumentException("Arrays must all be of the same length"); } for (int i=0; i < length; i++) { // Could do this bit with LINQ if you wanted T[] result = new T[sources.Length]; for (int j=0; j < result.Length; j++) { result[j] = sources[j][i]; } yield return result; } } 

那么调用代码将是:

 foreach (var array in Zip(columns, row, whatevers)) { // column = array[0] // value = array[1] // whatever = array[2] } 

当然,这涉及一定数量的复制 - 您每次都在创建一个数组。 您可以通过引入另一种类型来改变它:

 public struct Snapshot { readonly T[][] sources; readonly int index; public Snapshot(T[][] sources, int index) { this.sources = sources; this.index = index; } public T this[int element] { return sources[element][index]; } } 

尽管如此,这可能被视为过度杀戮;)

我可以不断提出各种想法,说实话......但基本要素是:

  • 通过一些可重用的工作,您可以使调用代码更好
  • 对于任意类型的组合,由于generics的工作方式,您必须分别执行每个参数数量(2,3,4 ...)
  • 如果您乐意为每个部件使用相同的类型,您可以做得更好

在函数式语言中,您通常会找到一个“zip”函数,它有望成为C#4.0的一部分。 Bart de Smet基于现有的LINQ函数提供了一个有趣的zip实现:

 public static IEnumerable Zip( this IEnumerable first, IEnumerable second, Func func) { return first.Select((x, i) => new { X = x, I = i }) .Join(second.Select((x, i) => new { X = x, I = i }), o => oI, i => iI, (o, i) => func(oX, iX)); } 

然后你可以这样做:

  int[] s1 = new [] { 1, 2, 3 }; int[] s2 = new[] { 4, 5, 6 }; var result = s1.Zip(s2, (i1, i2) => new {Value1 = i1, Value2 = i2}); 

如果你真的使用数组,最好的方法可能只是使用带索引的传统for循环。 不是很好,被授予,但据我所知,.NET没有提供更好的方法来做到这一点。

您还可以将代码封装到名为zip的方法中 – 这是一种常见的高阶列表函数。 但是,C#缺少合适的元组类型,这是相当苛刻的。 你最终会返回一个IEnumerable> ,这不是很好。

顺便说一句,你真的使用IEnumerable而不是IEnumerable或者为什么要转换Current值?

使用IEnumerator两者都会很好

 var currentValues = currentRow.Split(separatorChar); using (IEnumerator valueEnum = currentValues.GetEnumerator(), columnEnum = columnList.GetEnumerator()) { while (valueEnum.MoveNext() && columnEnum.MoveNext()) valueMap.Add(columnEnum.Current, valueEnum.Current); } 

或者创建一个扩展方法

 public static IEnumerable Zip(this IEnumerable source, IEnumerable other, Func selector) { using (IEnumerator sourceEnum = source.GetEnumerator()) { using (IEnumerator otherEnum = other.GetEnumerator()) { while (sourceEnum.MoveNext() && columnEnum.MoveNext()) yield return selector(sourceEnum.Current, otherEnum.Current); } } } 

用法

 var currentValues = currentRow.Split(separatorChar); foreach (var valueColumnPair in currentValues.Zip(columnList, (a, b) => new { Value = a, Column = b }) { valueMap.Add(valueColumnPair.Column, valueColumnPair.Value); } 

您可以创建一个二维数组或一个字典(这会更好),而不是创建两个单独的数组。 但实际上,如果它有效,我不会试图改变它。