Возможно ли обратиться к элементу коллекции по индексу внутри LINQ-запроса?
Прохожу тему LINQ в своем обучении. Дано задание выполнить несколько простых операций со строками, написав всего один LINQ-запрос.
Вопрос следующий. В рамках данной задачи необходимо пропустить первую строку (заголовок), затем каждую из строк разделить на три части, выполнить парсинг двух частей из них (третья должна остаться строкой), а затем создать и вернуть объект с полями, которым присвоены результирующие значения. Но как это можно сделать не имея возможность обратиться к каждой из трех частей разделенной строки по индексу, я не понимаю.
Подскажите, пожалуйста, знатоки LINQ, как обычно решаются подобные задачи, и как можно написать решение в данном случае?
Пример входящей строки:
SlideId;SlideType;UnitTitle
0;theory;Первое знакомство с C#
1;quiz;Первое знакомство с C#
2;theory;Первое знакомство с C#
3;exercise;Первое знакомство с C#
Код метода:
public static IDictionary<int, SlideRecord> ParseSlideRecords(IEnumerable<string> lines)
{
return ...
}
Ответы (2 шт):
Запрос можно покороче и попроще сделать:
// .NET Framework 4.8
using System;
using System.Linq;
using System.Collections.Generic;
enum SlideType { theory, quiz, exercise }
class SlideRecord {
public SlideType SlideType;
public string UnitTitle;
public SlideRecord(string[] slide)
{ Enum.TryParse<SlideType>(slide[1], out SlideType); UnitTitle = slide[2]; }
public override string ToString() { return "#" + SlideType + " " + UnitTitle; }
}
static class Program {
public static IDictionary<int, SlideRecord> ParseSlideRecords(IEnumerable<string> lines) {
int itp; SlideType etp;
return lines.Skip(1).Select(s => s.Split(';'))
.Where(w => int.TryParse(w[0], out itp) // проверка первого элемента
&& Enum.TryParse<SlideType>(w[1], out etp)) // проверка второго элемента
.ToDictionary(t => int.Parse(t[0]), t => new SlideRecord(t)); // используем удобный конструктор
}
static void Main(string[] args) {
var input = new string[] { "SlideId;SlideType;UnitTitle",
"0;theory;Первое знакомство с C#",
"1;quiz;Первое знакомство с C#",
"2в1;theory;Первое знакомство с C#",
"3;exercise;Первое знакомство с C#" };
foreach (var kvp in ParseSlideRecords(input))
Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
}
}
==>
0: #theory Первое знакомство с C#
1: #quiz Первое знакомство с C#
3: #exercise Первое знакомство с C#
P. S. Добавил .Where(w => int.TryParse(w[0], out itp))
для отбора корректных значений. В данном случае оно не будет пропускать и первую строку, и .Skip(1)
можно убрать. Но для общего применения .Skip(1)
нужно оставить.
Запускал на .NET 8. В данном решении валидации на значения нет, т. к. по задаче это не требуется. На вход используется именно строка, как написано по задаче, а не строковый массив. Вывод в консоль через тип record.
class Program
{
public enum SlideType {
Theory,
Quiz,
Exercise,
}
public record SlideRecord
{
public SlideType SlideType { get; set; }
public string Message { get; set; }
}
public static IDictionary<int, SlideRecord> ParseSlideRecords(IEnumerable<string> lines)
{
const int id = 0;
const int slideType = 1;
const int message = 2;
const string separator = ";";
return lines.Skip(1)
.Select(line => line.Split(separator))
.ToDictionary(line => int.Parse(line[id]), line => new SlideRecord
{
SlideType = Enum.Parse<SlideType>(line[slideType], true),
Message = line[message],
});
}
static void Main(string[] args)
{
// Входящая строка
var input = """
SlideId;SlideType;UnitTitle
0;theory;Первое знакомство с C#
1;quiz;Первое знакомство с C#
2;theory;Первое знакомство с C#
3;exercise;Первое знакомство с C#
""";
// Разбиваем на массив, который подойдёт для метода
var lines = input.Split(Environment.NewLine);
// Вызываем метод, получаем результат
var slides = ParseSlideRecords(lines);
// Выводим на консоль
foreach(var slide in slides)
Console.WriteLine($"{slide.Key}: {slide.Value}");
}
}