Поскольку все, что вам когда-либо нужно, - это последовательные string
с, а не вся коллекция, чтобы создать пару , вы можете вручную перечислить два запроса за раз, используя метод итератора до yield
результатов, как только они станут доступны ...
static IEnumerable<KeyValuePair<string, string>> ExtractPairsByEnumeration(IEnumerable<string> items)
{
using (var itemEnumerator = items.GetEnumerator())
{
while (itemEnumerator.MoveNext())
{
string name = itemEnumerator.Current;
if (!itemEnumerator.MoveNext())
{
// We received a name item with no following value item
// Use whatever default value you want here
yield return new KeyValuePair<string, string>(name, "<none>");
yield break;
}
else
yield return new KeyValuePair<string, string>(name, itemEnumerator.Current);
}
}
}
Вы бы назвали это так ...
using System;
using System.Collections.Generic;
using System.Linq;
namespace SO60748447
{
class Program
{
private const string InputText = @"
Client Name:
Siemens
Client ID:
7000002
Batch File Name:
6030001030-20200303-00005470
File Status:
Successful
Sent
7
Batch File ID:
0008615020
Date Uploaded:
03-Mar-2020
Successful
7
Creator:
STP-SIEMENSCORPOR
Failed
0";
static void Main()
{
string[] inputLines = InputText.Trim().Split("\r\n");
IEnumerable<string>[] testQueries = new IEnumerable<string>[] {
inputLines,
inputLines.Take(inputLines.Length - 1)
};
foreach (IEnumerable<string> query in testQueries)
{
Console.WriteLine($"Extracting {query.Count()} input lines:");
foreach (KeyValuePair<string, string> pair in ExtractPairsByEnumeration(query))
Console.WriteLine($"\t{pair}");
Console.WriteLine();
}
}
}
}
.. .which производит вывод как это ...
Extracting 20 input lines:
[Client Name:, Siemens]
[Client ID:, 7000002]
[Batch File Name:, 6030001030-20200303-00005470]
[File Status:, Successful]
[Sent, 7]
[Batch File ID:, 0008615020]
[Date Uploaded:, 03-Mar-2020]
[Successful, 7]
[Creator:, STP-SIEMENSCORPOR]
[Failed, 0]
Extracting 19 input lines:
[Client Name:, Siemens]
[Client ID:, 7000002]
[Batch File Name:, 6030001030-20200303-00005470]
[File Status:, Successful]
[Sent, 7]
[Batch File ID:, 0008615020]
[Date Uploaded:, 03-Mar-2020]
[Successful, 7]
[Creator:, STP-SIEMENSCORPOR]
[Failed, <none>]
Обратите внимание, что он все еще производит разумный вывод во втором случае, если дано нечетное количество строк.
Альтернатива LINQ, которая также требует только IEnumerable<>
должен использовать метод Aggregate()
. Для этого требуется делегат, которому передается каждый элемент входной последовательности, и аккумулятор (т. Е. list
), который вы используете для сохранения результатов до этой точки ...
static List<KeyValuePair<string, string>> ExtractPairsByAggregation(IEnumerable<string> items)
{
return items
// Aggregate() doesn't have an overload that provides the index,
// so package it together with each item in a ValueTuple using Select()
.Select((item, index) => (item, index))
.Aggregate(
// Storage for both intermediate and final results
new List<KeyValuePair<string, string>>(),
(list, current) => {
if (current.index % 2 == 0) // Even items are a name
{
KeyValuePair<string, string> newPair = new KeyValuePair<string, string>(
current.item, "<none>"
);
// Add a partial pair as soon as it's encountered
// so it's still present in the results even if
// this is the last item in the sequence
list.Add(newPair);
}
else // Odd items are a value
{
// The last pair in the list is the corresponding partial pair
int pairIndex = list.Count - 1;
KeyValuePair<string, string> oldPair = list[pairIndex];
// KeyValuePair<> is immutable, so recreate it now that we have the value
KeyValuePair<string, string> newPair = new KeyValuePair<string, string>(
oldPair.Key, current.item
);
list[pairIndex] = newPair;
}
// Return the same list so it is available for the
// next item in the sequence and as the final result
return list;
}
);
}
В отличие от ExtractPairsByEnumeration()
, это возвращает полные результаты, когда они все доступны, а не по одному за раз. Если вы вызываете это в методе Main()
, указанном выше, результат будет тем же.
Кстати, если предположить, что это классы из HTML Agility Pack, который вы используете, то вызывать cell.InnerText.ToString()
не нужно, потому что InnerText
, как следует из названия, уже string
, но если вы настаиваете на его вызове, вы должны использовать let
предложение , чтобы вы могли вызвать его один раз и повторно использовать результат ...
[snip]
from cell in row.SelectNodes("th|td")
let cellText = cell.InnerText.ToString()
where !string.IsNullOrEmpty(cellText)
&& cellText != "File Summary"
&& cellText != "Payment Instructions"
&& cellText != "Number"
select cell.InnerText;// This should probably be cellText as well