Member 13506556 Ответов: 2

C# загрузить csv-файл и сгруппировать по и агрегировать результаты


если у меня есть файл:

name    state      ClosetSize    childGender
Hank    OK         2               M
Hank    OK         1               F
Wilbur  TX         1               F
Wilbur  TX         2               F
Tom     CA         3               M
Tom     CA         4               M
Zed     VT         1               M
Zed     VT         2               M
Zed     VT         3               F


Я хочу вывести файл, если объединенный общий размер шкафа только у тех людей, у которых есть как минимум одна женщина (F), так и хотя бы одна (M). Затем выведите его в другой csv-файл.

Я бы ожидал, что это будут только Хэнк и Зед

Что я уже пробовал:

Я пробовал загрузить это в список и использовать Linq

Richard Deeming

Где код, который вы пробовали, и в чем была проблема с ним?

Member 13506556

ищу хорошую структуру для начала. Загрузка, агрегатирование, экспорт. У меня нет подходящей отправной точки.

2 Ответов

Рейтинг:
6

Richard Deeming

Другой вариант, используя CsvHelper[^]:

struct SourceRecord
{
    public string name { get; set; }
    public string state { get; set; }
    public int ClosetSize { get; set; }
    public char childGender { get; set; }
    
    public static IReadOnlyCollection<SourceRecord> Load(TextReader reader)
    {
        var csv = new CsvReader(reader);
        return csv.GetRecords<SourceRecord>().ToList();
    }
    
    public static IReadOnlyCollection<SourceRecord> LoadFrom(string fileName)
    {
        using (var reader = File.OpenText(fileName))
        {
            return Load(reader);
        }
    }
}

struct OutputRecord
{
    public string name { get; set; }
    public string state { get; set; }
    public int ClosetSize { get; set; }
    
    public static void Save(TextWriter writer, IEnumerable<OutputRecord> records)
    {
        var csv = new CsvWriter(writer);
        csv.WriteRecords(records);
    }
    
    public static void SaveTo(string fileName, IEnumerable<OutputRecord> records)
    {
        using (var writer = new StreamWriter(fileName))
        {
            Save(writer, records);
        }
    }
}

static void Process(string inputFile, string outputFile)
{
    IReadOnlyCollection<SourceRecord> input = SourceRecord.LoadFrom(inputFile);
    
    IEnumerable<OutputRecord> output = input
        .GroupBy(r => new { r.name, r.state })
        .Where(g => g.Any(r => r.childGender == 'M') && g.Any(r => r.childGender == 'F'))
        .Select(g => new OutputRecord
        {
            name = g.Key.name,
            state = g.Key.state,
            ClosetSize = g.Sum(r => r.ClosetSize),
        });
    
    OutputRecord.SaveTo(outputFile, output);
}

Выход:
name,state,ClosetSize
Hank,OK,3
Zed,VT,6


Member 13506556

это похоже на то, с чем я могу работать. Я только столкнулся с ошибкой на линии:
возвращение в формате CSV.GetRecords&ЛТ;sourcerecord и GT;().Список();


Ошибка CS1061 'object' не содержит определения для 'ToList' и не может быть найден метод расширения 'ToList', принимающий первый аргумент типа 'object' (отсутствует ли директива using или ссылка на сборку?

Я в том числе использую system.Linq;

Richard Deeming

Это прекрасно работает для меня: Демонстрация[^]

Member 13506556

понял. Спасибо!

Рейтинг:
1

Karthik_Mahalingam

пробовать

DataTable dt = new DataTable();
           dt.Columns.Add("name");
           dt.Columns.Add("state");
           dt.Columns.Add("ClosetSize");
           dt.Columns.Add("childGender");
           dt.Rows.Add("Hank", "OK", 2, "M");
           dt.Rows.Add("Hank", "OK", 1, "F");
           dt.Rows.Add("Wilbur", "tx", 1, "F");
           dt.Rows.Add("Wilbur", "tx", 2, "F");
           dt.Rows.Add("Tom", "ca", 3, "M");
           dt.Rows.Add("Tom", "ca", 4, "M");
           dt.Rows.Add("Zed", "VT", 1, "M");
           dt.Rows.Add("Zed", "VT", 2, "M");
           dt.Rows.Add("Zed", "VT", 3, "F");

           DataTable dtOutput = dt.Clone();
           string[] names = dt.Rows.OfType<DataRow>().Select(k => k["name"] + "").Distinct().ToArray();
           foreach (string name in names)
           {
               var tempRows = dt.Select("name='" + name + "'");
               if (tempRows.Length > 1)
               {
                   string[] genders = tempRows.Select(k => k["childGender"] + "").Distinct().ToArray();
                   if (genders.Contains("M") && genders.Contains("F"))
                       foreach (DataRow row in tempRows)
                           dtOutput.Rows.Add(row.ItemArray);
               }
           }


об этом конвертировать CSV в таблицу и наоборот
Чтение CSV - файла в таблицу данных[^]
Экспорт данных в CSV с помощью метода расширения[^]