对列表数组进行排序并根据列值删除重复行
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" 这样的情况,我会选择任何一行,因为它们都是相同的。
我想知道:
- 在哪里进行解析操作比较好?在
FileHelper
引擎输出或列表数组上?
- 是否有一种方法已经完成了我想做的事情?
- 如果没有,您能否概述一下您会推荐的策略?
作为示例,您可以在下面找到我在上面发布的 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;
}
}
}
我有一个包含大约 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" 这样的情况,我会选择任何一行,因为它们都是相同的。
我想知道:
- 在哪里进行解析操作比较好?在
FileHelper
引擎输出或列表数组上? - 是否有一种方法已经完成了我想做的事情?
- 如果没有,您能否概述一下您会推荐的策略?
作为示例,您可以在下面找到我在上面发布的 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;
}
}
}