同じ長さのリストが 2 つあるのですが、これら 2 つのリストを同時にループすることはできますか?
以下を実行するための正しい構文を探しています
foreach itemA, itemB in ListA, ListB
{
Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}
これはC#で可能だと思いますか? もしそうなら、これに相当するラムダ式は何ですか?
[編集]: 明確にする; これは、インデクサーを使用できない一般的な LINQ /IEnumerable<T>コンテキストで役立ちます。これは、a: 列挙型に存在せず、b: データを複数回読み取れることを保証できないためです。OP はラムダについて言及しているため、LINQ はそれほど遠くない可能性があります (そうです、LINQ とラムダはまったく同じではないことを認識しています)。
行方不明の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);
}
}
}
代わりに、単純な古い for ループで実行する方がはるかに簡単です...
for(int i=0; i<ListA.Length; i++)
{
Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}
明示的に行うことができます。
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() を検索するだけです。
プレーンな古い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);
}
Senthil Kumar の技術ブログには、 (Python) Itertools for C#の実装をカバーするシリーズがあり、itertools.izip.
Itertools for C# - Cycle and Zipから、任意の数のイテラブル( List<T>だけでなく)に対するソリューションが得られます。各反復で が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;
}
}
このコードは、すべての iterable の列挙子を取得し、すべての列挙子を前方に移動し、それらの現在の値を配列に累積して、配列を生成します。列挙子のいずれかが要素を使い果たすまで、これを行います。