一次循环2个列表

时间:2008-10-28 06:51:51

标签: c# list

我有两个长度相同的列表,是否可以一次遍历这两个列表?

我正在寻找正确的语法来执行以下操作

foreach itemA, itemB in ListA, ListB
{
  Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}

你觉得这在C#中有可能吗?如果是的话,lambda表达式与此相当的是什么?

8 个答案:

答案 0 :(得分:25)

[编辑]:澄清;这在通用的LINQ / IEnumerable<T>上下文中非常有用,在那里你不能使用一个索引器,因为a:它不存在于可枚举中,而b:你不能保证您可以多次读取数据。由于OP提到lambdas,LINQ可能不会太远(并且是的,我确实意识到LINQ和lambdas不是完全相同的事情)。

听起来你需要缺少Zip运算符;你可以欺骗它:

static void Main()
{
    int[] left = { 1, 2, 3, 4, 5 };
    string[] right = { "abc", "def", "ghi", "jkl", "mno" };

    // using KeyValuePair<,> approach
    foreach (var item in left.Zip(right))
    {
        Console.WriteLine("{0}/{1}", item.Key, item.Value);
    }

    // using projection approach
    foreach (string item in left.Zip(right,
        (x,y) => string.Format("{0}/{1}", x, y)))
    {
        Console.WriteLine(item);
    }
}

// library code; written once and stuffed away in a util assembly...

// returns each pais as a KeyValuePair<,>
static IEnumerable<KeyValuePair<TLeft,TRight>> Zip<TLeft, TRight>(
    this IEnumerable<TLeft> left, IEnumerable<TRight> right)
{
    return Zip(left, right, (x, y) => new KeyValuePair<TLeft, TRight>(x, y));
}

// accepts a projection from the caller for each pair
static IEnumerable<TResult> Zip<TLeft, TRight, TResult>(
    this IEnumerable<TLeft> left, IEnumerable<TRight> right,
    Func<TLeft, TRight, TResult> selector)
{
    using(IEnumerator<TLeft> leftE = left.GetEnumerator())
    using (IEnumerator<TRight> rightE = right.GetEnumerator())
    {
        while (leftE.MoveNext() && rightE.MoveNext())
        {
            yield return selector(leftE.Current, rightE.Current);
        }
    }
}

答案 1 :(得分:11)

在普通的旧循环中做它会简单得多......

for(int i=0; i<ListA.Length; i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}

答案 2 :(得分:5)

你可以明确地做到。

IEnumerator ListAEnum = ListA.GetEnumerator();
IEnumerator ListBEnum = ListB.GetEnumerator();

ListBEnum.MoveNext();
while(ListAEnum.MoveNext()==true)
{
  itemA=ListAEnum.getCurrent();
  itemB=ListBEnum.getCurrent();
  Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}

至少这个(或类似的东西)是编译器为foreach循环所做的事情。我没有测试过它,我猜测枚举器缺少一些模板参数。

从List和IEnumerator-Interface中查找GetEnumerator()。

答案 3 :(得分:1)

我建议使用普通的旧for循环,但你应该考虑不同的数组长度。 所以

for(int i=0; i<ListA.Length; i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}

可以变成

for(int i = 0; i < Math.Min(ListA.Length, ListB.Lenght); i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}

甚至进入

    for(int i = 0; i < Math.Max(ListA.Length, ListB.Lenght); i++)
    {
        string valueA = i < ListA.Length ? listA[i].ToString() : "";
        string valueB = i < ListB.Length ? listB[i].ToString() : "";

        Console.WriteLine(valueA+ ", " + valueB);
    }

答案 4 :(得分:1)

我遇到了同样的问题,但是使用了列表中包含列表的对象..为了它的价值,这可能会帮助有同样问题的人。

由于IndexOf是O(n),因此运行时间不是很好,但是当时我正在处理比本例中更多的内部 - foreach循环,所以我不想处理迭代器变量。

在这种情况下,我非常想念PHPs foreach($ arrayList为$ key =&gt; $ value)表示法...也许我在C#中缺少一些东西,必须有办法在O中获取索引(c)时间! (遗憾的是这篇文章说不:Getting the array key in a 'foreach' loop

class Stock {
   string symbol;
   List<decimal> hourlyPrice; // provides a list of 24 decimals
}

// get hourly prices from yesterday and today
List<Stock> stockMondays = Stocks.GetStock("GOOGL,IBM,AAPL", DateTime.Now.AddDay(-1));
List<Stock> stockTuesdays = Stocks.GetStock("GOOGL,IBM,AAPL", DateTime.Now);

try {
    foreach(Stock sMonday in stockMondays) {
        Stock sTuesday = stockTuesday[stockMondays.IndexOf(sMonday)];

        foreach(decimal mondayPrice in sMonday.prices) {
            decimal tuesdayPrice = sTuesday.prices[sMonday.prices.IndexOf(mondayPrice)];
            // do something now
        }

    }
} catch (Exception ex) { // some reason why list counts aren't matching? }

答案 5 :(得分:0)

Senthil Kumar's tech blog,有一系列报道(Python)Itertools for C#的实现,包括itertools.izip

Itertools for C# - Cycle and Zip开始,您可以获得任意数量的 iterables (不仅仅是 List&lt; T&gt; )的解决方案。请注意,Zip在每次迭代时产生Array

public static IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)
{
IEnumerator<T>[] enumerators = Array.ConvertAll(iterables, (iterable) =>   iterable.GetEnumerator());

while (true)
{
   int index = 0;
   T[] values = new T[enumerators.Length];

   foreach (IEnumerator<T> enumerator in enumerators)
   {
       if (!enumerator.MoveNext())
          yield break;

        values[index++] = enumerator.Current;
   }

   yield return values;
}

}

  

代码获取所有迭代的枚举数,向前移动所有枚举数,将其当前值累积到数组中并生成数组。它会在任何一个枚举器耗尽元素之前执行此操作。

答案 6 :(得分:0)

我有这个小功能,它可以帮助我遍历这两个列表对象。 schema的类型为SqlData,它是一个包含三个属性的类。数据是一个包含动态类型值的列表。首先,我遍历模式集合,而不是使用item的索引来遍历数据对象。

        <table class='table table-responsive table-hover table-responsive  draggable'  border='5'>
        <thead>
            <tr class='warning'>
                <input id='searchAjaxText' type='text' class='form-control' placeholder='رقم الهاتف أو اسم المستخدم أو الايميل'/>
            </tr>
        </thead>
        <tbody id='searchAjaxResult'>

        </tbody>
    </table>

答案 7 :(得分:0)

现代答案

LINQ 现在有一个 built-in Zip method,因此您无需创建自己的。结果序列与最短输入一样长。 Zip 当前(从 .NET Core 3.0 开始)有 2 个重载。更简单的返回一个元组序列。它让我们可以生成一些非常接近原始请求的非常简洁的代码:

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };

foreach (var (number, word) in numbers.Zip(words))
    Console.WriteLine($"{number}, {word}");

// 1, one
// 2, two
// 3, three

或者,同样的事情,但没有元组解包:

foreach (var item in numbers.Zip(words))
    Console.WriteLine($"{item.First}, {item.Second}");

另一个重载(早于 Core 3.0 出现)采用映射函数,让您可以更好地控制结果。此示例返回一个字符串序列,但您可以返回任何内容的序列(例如某些自定义类)。

var numbersAndWords = numbers.Zip(words, (number, word) => $"{number}, {word}");

foreach (string item in numbersAndWords)
    Console.WriteLine(item);

如果您在常规对象上使用 LINQ(而不是使用它来生成 SQL),您还可以查看 MoreLINQ,它提供了多种压缩方法。这些方法中的每一个都需要多达 4 个输入序列,而不仅仅是 2 个:

  • EquiZip - 如果输入序列的长度不同,则会引发异常。

  • ZipLongest - 结果序列将始终与最长的输入序列一样长,其中每个较短序列元素类型的默认值用于填充。

  • ZipShortest - 结果序列与最短输入序列一样短。

有关用法,请参阅他们的 examples 和/或 tests。似乎 MoreLINQ 的压缩比常规 LINQ 早,所以如果你坚持使用旧版本的 .NET,它可能是一个不错的选择。

相关问题