[英]Iterate two Lists or Arrays with one ForEach statement in C#
这只是一般知识:
如果我有两个,比如说List ,并且我想用相同的 foreach 循环迭代两个,我们可以这样做吗?
编辑
只是为了澄清,我想这样做:
List<String> listA = new List<string> { "string", "string" };
List<String> listB = new List<string> { "string", "string" };
for(int i = 0; i < listA.Count; i++)
listB[i] = listA[i];
但是使用 foreach =)
这称为Zip操作,将在 .NET 4 中得到支持。
有了这个,你就可以写出类似的东西:
var numbers = new [] { 1, 2, 3, 4 };
var words = new [] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w });
foreach(var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
作为具有命名字段的匿名类型的替代方案,您还可以通过使用元组及其静态 Tuple.Create 助手来节省大括号:
foreach (var nw in numbers.Zip(words, Tuple.Create))
{
Console.WriteLine(nw.Item1 + nw.Item2);
}
如果您不想等待 .NET 4.0,您可以实现自己的Zip
方法。 以下适用于 .NET 2.0。 您可以根据您希望如何处理两个枚举(或列表)具有不同长度的情况来调整实现; 这个继续到较长枚举的末尾,返回较短枚举中缺失项的默认值。
static IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> first, IEnumerable<U> second)
{
IEnumerator<T> firstEnumerator = first.GetEnumerator();
IEnumerator<U> secondEnumerator = second.GetEnumerator();
while (firstEnumerator.MoveNext())
{
if (secondEnumerator.MoveNext())
{
yield return new KeyValuePair<T, U>(firstEnumerator.Current, secondEnumerator.Current);
}
else
{
yield return new KeyValuePair<T, U>(firstEnumerator.Current, default(U));
}
}
while (secondEnumerator.MoveNext())
{
yield return new KeyValuePair<T, U>(default(T), secondEnumerator.Current);
}
}
static void Test()
{
IList<string> names = new string[] { "one", "two", "three" };
IList<int> ids = new int[] { 1, 2, 3, 4 };
foreach (KeyValuePair<string, int> keyValuePair in ParallelEnumerate(names, ids))
{
Console.WriteLine(keyValuePair.Key ?? "<null>" + " - " + keyValuePair.Value.ToString());
}
}
您可以使用 Union 或 Concat,前者删除重复项,后者不删除
foreach (var item in List1.Union(List1))
{
//TODO: Real code goes here
}
foreach (var item in List1.Concat(List1))
{
//TODO: Real code goes here
}
从 C# 7 开始,您可以使用元组...
int[] nums = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three", "four" };
foreach (var tuple in nums.Zip(words, (x, y) => (x, y)))
{
Console.WriteLine($"{tuple.Item1}: {tuple.Item2}");
}
// or...
foreach (var tuple in nums.Zip(words, (x, y) => (Num: x, Word: y)))
{
Console.WriteLine($"{tuple.Num}: {tuple.Word}");
}
这是一个自定义的 IEnumerable<> 扩展方法,可用于同时循环遍历两个列表。
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
public static class LinqCombinedSort
{
public static void Test()
{
var a = new[] {'a', 'b', 'c', 'd', 'e', 'f'};
var b = new[] {3, 2, 1, 6, 5, 4};
var sorted = from ab in a.Combine(b)
orderby ab.Second
select ab.First;
foreach(char c in sorted)
{
Console.WriteLine(c);
}
}
public static IEnumerable<Pair<TFirst, TSecond>> Combine<TFirst, TSecond>(this IEnumerable<TFirst> s1, IEnumerable<TSecond> s2)
{
using (var e1 = s1.GetEnumerator())
using (var e2 = s2.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
yield return new Pair<TFirst, TSecond>(e1.Current, e2.Current);
}
}
}
}
public class Pair<TFirst, TSecond>
{
private readonly TFirst _first;
private readonly TSecond _second;
private int _hashCode;
public Pair(TFirst first, TSecond second)
{
_first = first;
_second = second;
}
public TFirst First
{
get
{
return _first;
}
}
public TSecond Second
{
get
{
return _second;
}
}
public override int GetHashCode()
{
if (_hashCode == 0)
{
_hashCode = (ReferenceEquals(_first, null) ? 213 : _first.GetHashCode())*37 +
(ReferenceEquals(_second, null) ? 213 : _second.GetHashCode());
}
return _hashCode;
}
public override bool Equals(object obj)
{
var other = obj as Pair<TFirst, TSecond>;
if (other == null)
{
return false;
}
return Equals(_first, other._first) && Equals(_second, other._second);
}
}
}
我经常需要对两个集合中的每一对执行一个操作。 Zip 方法在这种情况下没有用。
可以使用此扩展方法ForPair :
public static void ForPair<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second,
Action<TFirst, TSecond> action)
{
using (var enumFirst = first.GetEnumerator())
using (var enumSecond = second.GetEnumerator())
{
while (enumFirst.MoveNext() && enumSecond.MoveNext())
{
action(enumFirst.Current, enumSecond.Current);
}
}
}
例如,你可以这样写:
var people = new List<Person> { person1, person2 };
var wages = new List<decimal> { 10, 20 };
people.ForPair(wages, (p, w) => p.Wage = w);
但是请注意,此方法不能用于修改集合本身。 例如,这将不起作用:
List<String> listA = new List<string> { "string", "string" };
List<String> listB = new List<string> { "string", "string" };
listA.ForPair(listA, (c1, c2) => c1 = c2); // Nothing will happen!
因此,在这种情况下,您自己问题中的示例可能是最好的方法。
不,您必须为此使用 for 循环。
for (int i = 0; i < lst1.Count; i++)
{
//lst1[i]...
//lst2[i]...
}
你不能做这样的事情
foreach (var objCurrent1 int lst1, var objCurrent2 in lst2)
{
//...
}
如果你想要一个元素和相应的元素,你可以做
Enumerable.Range(0, List1.Count).All(x => List1[x] == List2[x]);
如果每个项目都等于第二个列表中的相应项目,则返回 true
如果这几乎但不完全是您想要的,那么如果您详细说明会有所帮助。
此方法适用于列表实现,并且可以作为扩展方法实现。
public void TestMethod()
{
var first = new List<int> {1, 2, 3, 4, 5};
var second = new List<string> {"One", "Two", "Three", "Four", "Five"};
foreach(var value in this.Zip(first, second, (x, y) => new {Number = x, Text = y}))
{
Console.WriteLine("{0} - {1}",value.Number, value.Text);
}
}
public IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(List<TFirst> first, List<TSecond> second, Func<TFirst, TSecond, TResult> selector)
{
if (first.Count != second.Count)
throw new Exception();
for(var i = 0; i < first.Count; i++)
{
yield return selector.Invoke(first[i], second[i]);
}
}
如果列表具有相同的长度,您也可以简单地使用局部整数变量:
List<classA> listA = fillListA();
List<classB> listB = fillListB();
var i = 0;
foreach(var itemA in listA)
{
Console.WriteLine(itemA + listB[i++]);
}
您还可以执行以下操作:
var i = 0;
foreach (var itemA in listA)
{
Console.WriteLine(itemA + listB[i++]);
}
注意:
listA
的长度必须与listB
的长度相同。
我理解/希望列表具有相同的长度:不,您唯一的赌注是使用普通的旧标准 for 循环。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.