Возможно ли обратиться к элементу коллекции по индексу внутри 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 шт):

Автор решения: rotabor

Запрос можно покороче и попроще сделать:

// .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) нужно оставить.

→ Ссылка
Автор решения: Vladislav Mikkoev

Запускал на .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}");
    }
}
→ Ссылка