Пропуск через 2 списка одновременно
У меня есть два списка одинаковой длины, можно ли сразу пропустить эти два списка?
Я ищу правильный синтаксис, чтобы сделать ниже
foreach itemA, itemB in ListA, ListB
{
Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}
Как вы думаете, это возможно на С#? И если это так, каково эквивалент лямбда-выражения?
Ответы
Ответ 1
[править]: уточнить; это полезно в общем контексте LINQ/ IEnumerable<T>
, где вы не можете использовать индексатор, потому что a: он не существует в перечислимом, и b: вы не можете гарантировать, что вы можете читать данные более одного раза. Поскольку OP упоминает лямбда, происходит, что 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);
}
}
}
Ответ 2
Проще будет просто сделать это в простой старой для цикла...
for(int i=0; i<ListA.Length; i++)
{
Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}
Ответ 3
Вы можете сделать это явным.
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. Я не тестировал его, и, наверное, некоторые параметры шаблона отсутствуют для счетчиков.
Просто найдите GetEnumerator() из списка и интерфейса IEnumerator.
Ответ 4
Я рекомендую использовать простой старый цикл, но вы должны учитывать разные длины массивов.
Итак,
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);
}
Ответ 5
У меня была эта же проблема, но с использованием списков объектов со списками внутри них.. для чего это стоит, это может помочь кому-то с той же проблемой.
Время выполнения этого не очень хорошо, так как IndexOf равно O (n), но в то время я занимаюсь гораздо более внутренними циклами foreach, чем в этом примере, поэтому я не хотел обрабатывать переменные итератора.
В то же время я очень скучаю по PHP foreach ($ arrayList как $key = > $value) нотация... может быть, я что-то пропустил в С#, должен быть способ получить индекс в O ( c) время!
(к сожалению, в этом сообщении нет: Получение ключа массива в цикле foreach)
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? }
Ответ 6
Технический блог Senthil Kumar, имеет серию, охватывающую реализацию (Python) Itertools для С#, включая itertools.izip
.
Из Itertools for С# - 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;
}
}
Код получает перечисление для всех итераций, перемещает все перечисления вперед, накапливает их текущие значения в массив и дает массив. Он делает это до тех пор, пока ни один из счетчиков не исчерпывается элементами.
Ответ 7
У меня есть эта небольшая функция, которая помогает мне перебирать эти два объекта списка. схема имеет тип SqlData, который является классом, который содержит три свойства. И данные - это список, который содержит значения динамического типа. Сначала я выполняю итерацию через сборку схемы, а не используя индекс элемента для итерации через объект данных.
public List<SqlData> SqlDataBinding(List<SqlData> schema, List<dynamic> data)
{
foreach (SqlData item in schema)
{
item.Values = data[schema.IndexOf(item)];
}
return schema
}