对列表数组进行排序并根据列值删除重复行

Sorting a List array and delete duplicate rows based on column values

我有一个包含大约 40 列的 CSV 文件。我使用 FileHelpers 来读取文件并提取单个列以便稍后在代码中执行其他操作。

提取的字符串列表包含重复的字符串元素,我使用 Distinct() 方法去除了它们。

一项新功能请求要求我提取额外的列,这些列是双精度而非字符串。目前,我正在将这些元素转换为字符串,并且正在构建一个列表数组。

包括这些额外的列后,消除重复项变得更加困难。 以前,我不关心重复系列中的哪个元素被保留。现在我有两个 selection 标准,基于另外两个列中的值

我准备了一个table来举例说明手头的问题

╔════════════╦════════════╦════════════╗
║ Property 1 ║ Property 2 ║ Property 3 ║
╠════════════╬════════════╬════════════╣
║ AAA        ║        100 ║       1000 ║
║ AAA        ║         50 ║        500 ║
║ AAA        ║         10 ║        800 ║
║ BBB        ║          5 ║         70 ║
║ BBB        ║         20 ║         20 ║
║ BBB        ║         18 ║         11 ║
║ CCC        ║         10 ║         13 ║
║ CCC        ║         10 ║        445 ║
║ CCC        ║          5 ║       1000 ║
║ DDD        ║          0 ║        100 ║
║ DDD        ║          0 ║        100 ║
║ DDD        ║          0 ║        100 ║
╚════════════╩════════════╩════════════╝

第一个排序标准是 属性1,这是我最初从 CSV 文件中提取的列。 第二个标准是 属性 2。 例如,元素 "AAA" 有 3 属性 2 个值:100、50 和 10。在这种情况下,我将保留具有 100 的一个并消除其他两个。 对于元素 "BBB",我将保留 属性 2 = 20

的行

如果 属性 2 有多个最大值(例如,元素 "CCC"),我会查看 属性 3 和 select 具有最高值。

最后,如果我有像 "DDD" 这样的情况,我会选择任何一行,因为它们都是相同的。

我想知道:

  1. 在哪里进行解析操作比较好?在 FileHelper 引擎输出或列表数组上?
  2. 是否有一种方法已经完成了我想做的事情?
  3. 如果没有,您能否概述一下您会推荐的策略?

作为示例,您可以在下面找到我在上面发布的 table 和预期结果虚拟数据集

╔════════════╦════════════╦════════════╗
║ Property 1 ║ Property 2 ║ Property 3 ║
╠════════════╬════════════╬════════════╣
║ AAA        ║        100 ║       1000 ║
║ AAA        ║         50 ║        500 ║
║ AAA        ║         10 ║        800 ║
║ BBB        ║          5 ║         70 ║
║ BBB        ║         20 ║         20 ║
║ BBB        ║         18 ║         11 ║
║ CCC        ║         10 ║         13 ║
║ CCC        ║         10 ║        445 ║
║ CCC        ║          5 ║       1000 ║
║ DDD        ║          0 ║        100 ║
║ DDD        ║          0 ║        100 ║
║ DDD        ║          0 ║        100 ║
╚════════════╩════════════╩════════════╝

所需的输出将是

╔════════════╦════════════╦════════════╗
║ Property 1 ║ Property 2 ║ Property 3 ║
╠════════════╬════════════╬════════════╣
║ AAA        ║        100 ║       1000 ║
║ BBB        ║         20 ║         20 ║
║ CCC        ║         10 ║        445 ║
║ DDD        ║          0 ║        100 ║
╚════════════╩════════════╩════════════╝

编辑 1 我包括了 FileHelper class 的代码片段和一个代表 FileHelper 引擎的样子的列表

FileHelper class 会像这样(只报告相关键)

[DelimitedRecord(",")]
    [IgnoreFirst(1)]
    class Test
    {
        public string property_1;
        public double property_2;
        public double property_3;
    }

读取 CSV 文件后,您将得到一个如下所示的列表(以上面的 table 为例)

static List<Test> test = new List<Test>
        {
                new Test {property_1 = "AAA", property_2 = 100, property_3 = 1000},
                new Test {property_1 = "AAA", property_2 = 50, property_3 = 500},
                new Test {property_1 = "AAA", property_2 = 10, property_3 = 800},
                new Test {property_1 = "BBB", property_2 = 5, property_3 = 70},
                new Test {property_1 = "BBB", property_2 = 20, property_3 = 20},
                new Test {property_1 = "BBB", property_2 = 18, property_3 = 11},
                new Test {property_1 = "CCC", property_2 = 10, property_3 = 13},
                new Test {property_1 = "CCC", property_2 = 10, property_3 = 445},
                new Test {property_1 = "CCC", property_2 = 5, property_3 = 1000},
                new Test {property_1 = "DDD", property_2 = 0, property_3 = 100},
                new Test {property_1 = "DDD", property_2 = 0, property_3 = 100},
                new Test {property_1 = "DDD", property_2 = 0, property_3 = 100},
        };

当前解决方案

基于jdweng代码,我写了如下

List<Test> sorted = test.AsEnumerable()
                .OrderByDescending(x => x.property_2).ThenByDescending(x => x.property_3).GroupBy(x => x.property_1).Select(x => x.First()).ToList();  

如果你想在控制台上看到结果,你可以使用以下命令打印它们

foreach (Test t in test)
{
    Console.WriteLine(t.property_1 + "\t" + t.property_2.ToString() + "\t" + t.property_3.ToString());
}

foreach (Test t in sorted)
{
    Console.WriteLine(t.property_1 + "\t" + t.property_2.ToString() + "\t" + t.property_3.ToString());
}

这里有一个使用辅助方法的方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;


namespace ConsoleApplication123
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Property 1", typeof(string));
            dt.Columns.Add("Property 2", typeof(int));
            dt.Columns.Add("Property 3", typeof(int));

            dt.Rows.Add(new object[] { "AAA", 100, 1000 });
            dt.Rows.Add(new object[] { "AAA", 50, 500 });
            dt.Rows.Add(new object[] { "AAA", 10, 800 });
            dt.Rows.Add(new object[] { "BBB", 5, 70 });
            dt.Rows.Add(new object[] { "BBB", 20, 20 });
            dt.Rows.Add(new object[] { "BBB", 18, 11 });
            dt.Rows.Add(new object[] { "CCC", 10, 13 });
            dt.Rows.Add(new object[] { "CCC", 10, 445 });
            dt.Rows.Add(new object[] { "CCC", 5, 1000 });
            dt.Rows.Add(new object[] { "DDD", 0, 100 });
            dt.Rows.Add(new object[] { "DDD", 0, 100 });
            dt.Rows.Add(new object[] { "DDD", 0, 100 });


            DataTable largest = dt.AsEnumerable()
                .OrderByDescending(x => x.Field<int>("Property 2"))
                .ThenByDescending(x => x.Field<int>("Property 3"))
                .GroupBy(x => x.Field<string>("Property 1"))
                .Select(x => x.First())
                .CopyToDataTable();

            DataTable results = dt.AsEnumerable()
                .GroupBy(x => x.Field<string>("Property 1")).Select(x => AddRowData(x.First(), x.ToArray())).CopyToDataTable();

        }
        static DataRow AddRowData(DataRow firstRow, DataRow[] allRows)
        {
            for (int col = 1; col < firstRow.ItemArray.Count(); col++)
            {
                firstRow[col] = allRows.Sum(x => x.Field<int>(col));
            }
            return firstRow;
        }
    }

}