Я бы, вероятно, сохранил ваши подсчеты в Dictionary<string, Dictionary<string , long>>
, а не в 2D-массиве. Тогда вам будет намного проще получить доступ к количеству каждого столбца.
Используя пакет NuGet CsvHelper
, мы можем создать класс для моделирования вашего CSV-файла. Единственное, с чем мы должны быть осторожны, - это выбирать правильные типы данных для ваших столбцов. Выбранные мной типы данных могут не подходить для вашей ситуации. Вы также можете найти документацию по API здесь .
public class CsvModel
{
[Name("")]
public string RowNumber { get; set; }
[Name("Location_Code")]
public string LocationCode { get; set; }
[Name("Location_Desc")]
public string LocationDesc { get; set; }
[Name("Type_Code")]
public long TypeCode { get; set; }
[Name("Fault_type")]
public string FaultType { get; set; }
[Name("Prod_Number")]
public long ProdNumber { get; set; }
public string Model { get; set; }
public string Causer { get; set; }
public string Auditor { get; set; }
public long Prio { get; set; }
[Name("Capture_Date")]
public DateTime CaptureDate { get; set; }
public long Steer { get; set; }
public long Engine { get; set; }
public long Country { get; set; }
[Name("Current shift number")]
public string CurrentShiftNumber { get; set; }
public string VIN { get; set; }
public string Comment { get; set; }
public string Shift { get; set; }
public long Year { get; set; }
[Name("Fault location C_Code")]
public string FaultLocationCCode { get; set; }
[Name("Fault location C_Desc")]
public string FaultLocationCDesk { get; set; }
[Name("Fault type C_Code")]
public string FaultTypeCCode { get; set; }
[Name("Fault type C_Desc")]
public string FaultTypeCDesc { get; set; }
[Name("Comment R")]
public string CommentR { get; set; }
[Name("Baumuster Sales desc.")]
public string BaumusterSalesDesc { get; set; }
[Name("Baumuster Technical desc.")]
public string BaumusterTechnicalDesc { get; set; }
public string T24 { get; set; }
}
Затем мы можем прочитать записи в IEnumerable<CsvMode>
с помощью GetRecords<T>
:
var path = "C:\\data.csv";
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords<CsvModel>();
Затем используйте отражение, чтобы получить количество столбцов в Dictionary<string, Dictionary<string , long>>
:
var recordCounts = new Dictionary<string, Dictionary<string, long>>();
foreach (var record in records)
{
var properties = record.GetType().GetProperties();
foreach (var property in properties)
{
var propertyName = property.Name;
if (!recordCounts.ContainsKey(propertyName))
{
recordCounts.Add(propertyName, new Dictionary<string, long>());
}
var propertyValue = property.GetValue(record, null);
var propertyKey = propertyValue.ToString();
if (propertyValue != null && !string.IsNullOrEmpty(propertyKey))
{
var count = recordCounts[propertyName].GetValueOrDefault(propertyKey, 0) + 1;
recordCounts[propertyName][propertyKey] = count;
}
}
}
Затем мы можем отсортировать количество столбцов в порядке убывания, создав новый словарь с LINQ:
var sortedRecordCounts = recordCounts
.ToDictionary(
kvp => kvp.Key,
kvp => new SortedDictionary<string, long>(
kvp.Value.OrderByDescending(kvp => kvp.Value)
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value)));
Который использует Enumerable.ToDictionary
для создания словарей (внутренний + внешний) и сортирует счетчики в порядке убывания с Enumerable.OrderByDescending
.
Мы также используем OrderedDictionary
, чтобы гарантировать порядок сортировки словаря, поскольку порядок Dictionary
не гарантируется.
Это также кратко упоминается в MSDN :
Порядок, в котором возвращаются элементы, не определен.
Мы можем ли мы затем выполнить итерацию этого словаря для отображения количества записей, которое также указывает, не было ли найдено действительных (пустых или нулевых) значений:
foreach (var kvp in sortedRecordCounts)
{
Console.WriteLine($"Column: {kvp.Key}");
if (kvp.Value.Count == 0)
{
Console.WriteLine("No values found");
}
foreach (var value in kvp.Value)
{
Console.WriteLine($"Value: {value.Key}, Count: {value.Value}");
}
Console.WriteLine();
}
Вывод:
Column: RowNumber
Value: 0, Count: 1
Value: 1, Count: 1
Value: 2, Count: 1
Column: LocationCode
Value: 09122, Count: 3
Column: LocationDesc
Value: Engine, Count: 2
Value: Transmission, Count: 1
Column: TypeCode
Value: 42, Count: 3
Column: FaultType
Value: Poor fit, Count: 3
Column: ProdNumber
Value: 7117215, Count: 1
Value: 7117235, Count: 1
Value: 7117237, Count: 1
Column: Model
Value: W205, Count: 3
Column: Causer
Value: Final 3, Count: 3
Column: Auditor
Value: Plant 1, WSA, Count: 3
Column: Prio
Value: 0, Count: 3
Column: CaptureDate
Value: 5/04/2019 12:00:00 AM, Count: 3
Column: Steer
Value: 1, Count: 3
Column: Engine
Value: 83, Count: 3
Column: Country
Value: 705, Count: 3
Column: CurrentShiftNumber
Value: T1220190404T0092, Count: 1
Value: T1220190404T0122, Count: 1
Value: T1220190404T0126, Count: 1
Column: VIN
Value: 55SWF8DB7KU316971, Count: 1
Value: 55SWF8DB2KU316991, Count: 1
Value: 55SWF8DB6KU316993, Count: 1
Column: Comment
No values found
Column: Shift
Value: A, Count: 3
Column: Year
Value: 2019, Count: 3
Column: FaultLocationCCode
No values found
Column: FaultLocationCDesk
No values found
Column: FaultTypeCCode
No values found
Column: FaultTypeCDesc
No values found
Column: CommentR
No values found
Column: BaumusterSalesDesc
Value: C 300, Count: 3
Column: BaumusterTechnicalDesc
Value: 205 E20 G, Count: 3
Column: T24
No values found
Обновление
Если вы хотите поддерживать несколько файлов CSV и не сохранять класс для столбцов (также избегая отражения), вы можете использовать решение generi c, например:
var path = "C:\\data.csv";
var recordCounts = new Dictionary<string, Dictionary<string, long>>();
using (var reader = new StreamReader(path))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Read();
csv.ReadHeader();
var headerRow = csv.Context.HeaderRecord;
if (string.IsNullOrEmpty(headerRow[0]))
{
headerRow[0] = "RowNumber";
}
foreach (var header in headerRow)
{
recordCounts.Add(header, new Dictionary<string, long>());
}
while (csv.Read())
{
foreach (var header in headerRow)
{
var headerKey = header == "RowNumber" ? string.Empty : header;
var columnValue = csv.GetField(headerKey);
if (!string.IsNullOrEmpty(columnValue))
{
var count = recordCounts[header].GetValueOrDefault(columnValue, 0) + 1;
recordCounts[header][columnValue] = count;
}
}
}
}
Использует метод чтения заголовков из Как лучше всего получить список имен столбцов с помощью CsvHelper? и использует предлагаемый метод Чтение вручную из документации CsvHelper
. Эти ресурсы и предложения были полезны @ Илиаром Турдушевым в комментариях.
Затем вы можете объединить вышеуказанное решение с запросом сортировки словаря LINQ и кодом печати, указанным выше, для получения аналогичных результатов.