CsvHelper - чтение в нескольких столбцах в один список

Я использую CSVHelper для чтения в большом количестве данных

Мне интересно, можно ли прочитать последние столбцы n и перенести их в список

"Name","LastName","Attribute1","Attribute2","Attribute3"

И сформируйте данные в нечто подобное

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public IList<string> Attributes { get; set; }
}

Я хочу сделать это за один шаг, я уверен, что у меня мог бы быть промежуточный шаг, на который я помещал объект с соответствующими свойствами атрибута, но было бы неплохо сделать это на одном-er

Ответ 1

Это делает трюк как картограф.

public sealed class PersonMap : CsvClassMap<Person>
{
    private List<string> attributeColumns = 
        new List<string> { "Attribute1", "Attribute2", "Attribute3" };

    public override void CreateMap()
    {
        Map(m => m.FirstName).Name("FirstName").Index(0);
        Map(m => m.LastName).Name("LastName").Index(1);
        Map(m => m.Attributes).ConvertUsing(row =>
            attributeColumns
                .Select(column => row.GetField<string>(column))
                .Where(value => String.IsNullOrWhiteSpace(value) == false)
            );
    }
}

Тогда вам просто нужно что-то вроде этого

using (var reader = new CsvReader(new StreamReader(filePath)))
{
    reader.Configuration.RegisterClassMap<PersonMap>();
    while (reader.Read())
    {
        var card = reader.GetRecord<Person>();
    }
}

Ответ 2

Я не знаю эту библиотеку, поэтому следующее может быть полезно или нет.

Если у вас уже есть IEnumerable<IEnumerable<string>>, который представляет все записи со всеми столбцами, вы можете использовать этот запрос Linq, чтобы получить List<Person> с помощью IList<string> Attributes:

IEnumerable<IEnumerable<string>> allRecords = .....;
IEnumerable<Person> allPersons = allRecords
.Select(rec => 
{
    var person = new Person();
    person.FirstName = rec.ElementAt(0);
    person.LastName = rec.ElementAtOrDefault(1);
    person.Attributes = rec.Skip(2).ToList();
    return person;
}).ToList();

Изменить. Загрузите библиотеку, следуя хотя бы компиляторам, не удалось ее проверить:

IList<Person> allPersons = new List<Person>();
using (var reader = new CsvHelper.CsvReader(yourTextReader))
{
    while (reader.Read())
    {
        var person = new Person();
        person.FirstName = reader.CurrentRecord.ElementAt(0);
        person.LastName = reader.CurrentRecord.ElementAtOrDefault(1);
        person.Attributes = reader.CurrentRecord.Skip(2).ToList();
        allPersons.Add(person);
    }
}

Ответ 3

Вам не нужно заранее определять имена столбцов, вы можете получить их из свойства FieldHeaders. Поэтому в более динамичной версии ответа Neil вы можете определить свой Mapper как таковой:

public sealed class PersonMap : CsvClassMap<Person> {
    public override void CreateMap() {
        Map(m => m.FirstName).Name("FirstName").Index(0);
        Map(m => m.LastName).Name("LastName").Index(1);
        Map(m => m.Attributes).ConvertUsing(row =>
            (row as CsvReader)?.FieldHeaders
                 .Where(header => header.StartsWith("Attribute"))
                 .Select(header => row.GetField<string>(header))
                 .Where(value => !string.IsNullOrWhiteSpace(value))
                 .ToList()
        );
    }
}

Ответ 4

Версия 3 поддерживает чтение и запись свойств IEnumerable. Вы можете использовать свойство IList<T> так же, как и вы. Вам просто нужно указать начальный индекс поля.

Map( m => m.Attributes ).Index( 2 );