Преобразовать результат linq в пару имя-значение - PullRequest
1 голос
/ 19 марта 2020

У меня есть запрос, который возвращает несколько строк. Первая строка - это имя, а вторая строка - фактическое значение. Конечным результатом является получение указанного c имени и добавление значения в строку с данными.

Код:

    var query = from table in doc.DocumentNode.SelectNodes("//table[@border='0']")
                from row in table.SelectNodes("tr")
                from cell in row.SelectNodes("th|td")
                where (!string.IsNullOrEmpty(cell.InnerText.ToString()))
                && cell.InnerText.ToString() != "File Summary"
                && cell.InnerText.ToString() != "Payment Instructions"
                && cell.InnerText.ToString() != "Number"
                select cell.InnerText;

    foreach (var cell in query)
    {
        logger.Info("{0}", cell);
    }

Результат

2020-03-18 15:29:04.5074 INFO Client Name:
2020-03-18 15:29:04.5764 INFO Siemens
2020-03-18 15:29:04.5764 INFO Client ID:
2020-03-18 15:29:04.5764 INFO 7000002
2020-03-18 15:29:04.5764 INFO Batch File Name:
2020-03-18 15:29:04.5764 INFO 6030001030-20200303-00005470
2020-03-18 15:29:04.5764 INFO File Status:
2020-03-18 15:29:04.5764 INFO Successful
2020-03-18 15:29:04.5764 INFO Sent
2020-03-18 15:29:04.5764 INFO 7
2020-03-18 15:29:04.5764 INFO Batch File ID:
2020-03-18 15:29:04.5764 INFO 0008615020
2020-03-18 15:29:04.5764 INFO Date Uploaded:
2020-03-18 15:29:04.5764 INFO 03-Mar-2020
2020-03-18 15:29:04.5764 INFO Successful
2020-03-18 15:29:04.5764 INFO 7
2020-03-18 15:29:04.5764 INFO Creator:
2020-03-18 15:29:04.5884 INFO STP-SIEMENSCORPOR
2020-03-18 15:29:04.5884 INFO Failed
2020-03-18 15:29:04.5884 INFO 0

В конце концов

string clientname = value[x]; or something similar

Попытка:

    var data = query.ToList();
    var obj = data.Select((item, index) =>
    {
        if (index < data.Count - 1 && index % 2 == 0)
            return new KeyValuePair<string, string>(item, data[index + 1]);
        return new KeyValuePair<string, string>(null, null);
    }).Where(x => x.Key != null);

Но для KeyValuePair значение obj равно нулю

Ответы [ 4 ]

3 голосов
/ 19 марта 2020

Вы можете попробовать что-то вроде этого:

List<string> data = new List<string>() { "Client Name", "Siemens", "Client ID", "7000002", "File Status", "Successful" };

var obj = data.Select((item, index) =>
 {
     if (index < data.Count - 1 && index % 2 == 0)
         return new KeyValuePair<string, string>(item, data[index + 1]);
     return new KeyValuePair<string, string>(null, null);
 }).Where(x => x.Key != null);

Вместо данных в вышеприведенном коде вы можете использовать вашу переменную: query

Это явно сбивает с толку, поэтому более простой способ :

Dictionary<string, string> map = new Dictionary<string, string>();
for (int idx = 0; idx < data.Count - 1; idx += 2)
{
    map[data[idx]] = data[idx + 1];
}
1 голос
/ 19 марта 2020

Вам нужен произвольный доступ, поэтому ваш первый шаг должен состоять в том, чтобы осознать результаты и сохранить их в памяти.

var list = query.ToList();

Получив доступ, вы можете получить доступ через индекс и собрать нужные вам строки.

var dictionary = Enumerable.Range(0, list.Count / 2)
    .ToDictionary
    (
        i => list[i * 2],
        i => list[i * 2 + 1]
    );
0 голосов
/ 19 марта 2020

Поскольку все, что вам когда-либо нужно, - это последовательные 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
0 голосов
/ 19 марта 2020

Я анализирую текстовые файлы более 45 лет. Код ниже работает, когда каждый элемент содержит 2 или более строк. Не похоже на другое решение. Использование мода 2 не будет работать в этом случае (или делится на 2)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            StreamReader reader = new StreamReader(FILENAME);
            string line = "";
            Dictionary<string, string> dict = new Dictionary<string, string>();
            string buffer = "";
            string key = "";
            Boolean first = true;
            while ((line = reader.ReadLine()) != null)
            {
                line = line.Trim();
                if (line.Length > 0)
                {
                    string[] splitLine = line.Split(new string[] { "INFO" }, StringSplitOptions.None).ToArray();
                    if (splitLine[1].Contains(":"))
                    {
                        if (!first)
                        {
                            dict.Add(key, buffer);

                        }
                        key = splitLine[1].Trim(new char[] { ' ', ':' });
                        buffer = "";
                        first = false;
                    }
                    else
                    {
                        if (buffer == string.Empty)
                        {
                            buffer = splitLine[1].Trim();
                        }
                        else
                        {
                            buffer += "," + splitLine[1].Trim();
                        }
                    }
                }
            }
            dict.Add(key, buffer);
            foreach (KeyValuePair<string, string> pair in dict)
            {
                Console.WriteLine("Key '{0}' : Value '{1}'", pair.Key, pair.Value);
            }
            Console.ReadLine();
        }
    }
}

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...