使用 GroupBy 将平面 List<T> 转换为 List<U> 以生成汇总数据
Convert flat List<T> to a List<U> using GroupBy to produce rolled up data
UPDATE 我还需要保留 List<T>
中 List<U>
中员工首次进入的索引以备后用。目前我正在使用从
下面的 修改的代码
List<EmployeeRollup> summary =
details
.GroupBy( e => e.EmployeeId , StringComparer.OrdinalIgnoreCase )
.Select( g => new EmployeeRollup {
EmployeeId = g.Key ,
ProjectDateFrom = g.Min( e => e.ProjectDate ) ,
ProjectDateThru = g.Max( e => e.ProjectDate ) ,
FullRecordsRef = employee
.FindIndex(f => f.employeeId == g.Key),
ProjectCodes = g.Select( e => e.ProjectCode )
.Distinct( StringComparer
.OrdinalIgnoreCase )
.ToArray() ,
}).ToList();
这个方法正确吗?有更有效的方法吗?
<- End Update ->
我有一个应用程序,我想将一个对象列表 List<T>
转换为另一个对象列表 List<U>
原始列表 (List<T>
) 是 List<Employee>
其中 Employee
定义为
class Employee{
public string empid;
public Date proj_date;
public string proj_code;
// other fields and methods
}
列表中的数据看起来像
empid proj_date proj_code
01 21/Nov/2014 02
01 21/Nov/2014 03
02 21/Nov/2014 09
02 22/Nov/2014 99
02 23/Nov/2014 09
03 21/Nov/2014 15
03 01/Dec/2014 16
我想将此 List<Employee>
转换为另一个列表,List<Emp2>
其中 Emp2
定义为
class Emp2{
public string empid;
public Date min_proj_date;
public Date max_proj_date;
public string [] proj_code;
// other fields and methods
}
从 List<Employee>
转换后 List<Emp2>
中的数据应如下所示
empid min_proj_date max_proj_date proj_code[]
01 21/Nov/2014 21/Nov/2014 [02, 03]
02 21/Nov/2014 23/Nov/2014 [09,99]
03 21/Nov/2014 01/Dec/2014 [15,16]
所以我正在做的是
- 按 employee_id 分组以获得员工的所有不同日期和 proj_codes。
- 获取日期的最小值和最大值
- 获取 proj_code 的不同值作为数组
我尝试使用 MoreLINQ 库中的 DistinctBy
函数)但无法解决问题。
您可以使用 GroupBy
和 Select
像这样从一个转换为另一个:
var myEmps = new List<Employee> { /* data here */ };
var myEmp2s = myEmps
.GroupBy(x => x.empid)
.Select(x => new Emp2
{
empid= x.Key,
min_project_date = x.Min(y => y.proj_date),
max_project_date = x.Max(y => y.proj_date),
proj_code = x.Select(y => y.proj_code).ToArray()
// Other fields are rolled up in a similar fashion as needed
});
主要有两种方法。第一个是用 list.Select(emp => new { foo = emp.Where(e => e.empid == emp.empid) })
保持 selecting 在你的初始列表上......但这在计算上很糟糕,并且不如...... GroupBy
.
惯用
看起来您还想要一些 OrderBy
和 Distinct
子句,Date
您可能是指 DateTime
?否则根据需要应用调整。
var empGroups = emp.GroupBy(e => e.empid);
empGroups.Select(g => new {
empid = g.Key,
min_proj_date = g.Min(e => e.proj_date.Date),
max_proj_date = g.Min(e => e.proj_date.Date),
proj_code = g.Select(e => e.proj_code).OrderBy(pc => pc).Distinct().ToArray()
})
通过第一次分组,您可以对分组的Key
进行操作,以供将来的汇总和selects使用。然后,您 select 一个新的输出类型,并且您在 属性 本身上有第二个内部 select。
请注意 GroupBy
结果的枚举类型是 IGrouping<(string) TKey, (Employee) TElement>
,其中 inherits from IEnumerable<TElement>
。捕获 g
后,它的类型现在是 IEnumerable<Employee>
加上对 g.Key
的访问权限。从那时起,就像对待任何其他 IEnumerable
一样对待它,并使用熟悉的 LINQ。这基本上是实例化变量、循环和添加到列表的替代方法;该变量在中定义并提升到您的 lambda 中。
并不比
复杂多少
public class Employee
{
public string EmployeeId ;
public DateTime ProjectDate ;
public string ProjectCode ;
}
public class EmployeeRollup
{
public string EmployeeId ;
public DateTime ProjectDateFrom ;
public DateTime ProjectDateThru ;
public string[] ProjectCodes ;
}
class Program
{
static void Main(string[] args)
{
List<Employee> details = new List<Employee>() ;
List<EmployeeRollup> summary =
details
.GroupBy( e => e.EmployeeId , StringComparer.OrdinalIgnoreCase )
.Select( g => new EmployeeRollup {
EmployeeId = g.Key ,
ProjectDateFrom = g.Min( e => e.ProjectDate ) ,
ProjectDateThru = g.Max( e => e.ProjectDate ) ,
ProjectCodes = g.Select( e => e.ProjectCode )
.Distinct( StringComparer
.OrdinalIgnoreCase )
.ToArray() ,
})
.ToList()
;
}
}
如果您想跟踪每个 Employee
实例在原始列表中的偏移量(位置),您可以这样做:
List<Employee> details = new List<Employee>() ;
int i = 0 ;
List<EmployeeRollup> summary =
details
.Select( e => new KeyValuePair<int,Employee>(i,e) )
.GroupBy( kvp => kvp.Value.EmployeeId , StringComparer.OrdinalIgnoreCase )
...
现在你有一个 KeyValuePair<int,Employee>
的分组,其中每个 KeyValuePair
的 Key
属性 是原始列表中的整数位置及其 Value
属性 是原始 Employee
实例。
只需进行由此产生的更改即可。
UPDATE 我还需要保留 List<T>
中 List<U>
中员工首次进入的索引以备后用。目前我正在使用从
List<EmployeeRollup> summary =
details
.GroupBy( e => e.EmployeeId , StringComparer.OrdinalIgnoreCase )
.Select( g => new EmployeeRollup {
EmployeeId = g.Key ,
ProjectDateFrom = g.Min( e => e.ProjectDate ) ,
ProjectDateThru = g.Max( e => e.ProjectDate ) ,
FullRecordsRef = employee
.FindIndex(f => f.employeeId == g.Key),
ProjectCodes = g.Select( e => e.ProjectCode )
.Distinct( StringComparer
.OrdinalIgnoreCase )
.ToArray() ,
}).ToList();
这个方法正确吗?有更有效的方法吗?
<- End Update ->
我有一个应用程序,我想将一个对象列表 List<T>
转换为另一个对象列表 List<U>
原始列表 (List<T>
) 是 List<Employee>
其中 Employee
定义为
class Employee{
public string empid;
public Date proj_date;
public string proj_code;
// other fields and methods
}
列表中的数据看起来像
empid proj_date proj_code
01 21/Nov/2014 02
01 21/Nov/2014 03
02 21/Nov/2014 09
02 22/Nov/2014 99
02 23/Nov/2014 09
03 21/Nov/2014 15
03 01/Dec/2014 16
我想将此 List<Employee>
转换为另一个列表,List<Emp2>
其中 Emp2
定义为
class Emp2{
public string empid;
public Date min_proj_date;
public Date max_proj_date;
public string [] proj_code;
// other fields and methods
}
从 List<Employee>
转换后 List<Emp2>
中的数据应如下所示
empid min_proj_date max_proj_date proj_code[]
01 21/Nov/2014 21/Nov/2014 [02, 03]
02 21/Nov/2014 23/Nov/2014 [09,99]
03 21/Nov/2014 01/Dec/2014 [15,16]
所以我正在做的是
- 按 employee_id 分组以获得员工的所有不同日期和 proj_codes。
- 获取日期的最小值和最大值
- 获取 proj_code 的不同值作为数组
我尝试使用 MoreLINQ 库中的 DistinctBy
函数)但无法解决问题。
您可以使用 GroupBy
和 Select
像这样从一个转换为另一个:
var myEmps = new List<Employee> { /* data here */ };
var myEmp2s = myEmps
.GroupBy(x => x.empid)
.Select(x => new Emp2
{
empid= x.Key,
min_project_date = x.Min(y => y.proj_date),
max_project_date = x.Max(y => y.proj_date),
proj_code = x.Select(y => y.proj_code).ToArray()
// Other fields are rolled up in a similar fashion as needed
});
主要有两种方法。第一个是用 list.Select(emp => new { foo = emp.Where(e => e.empid == emp.empid) })
保持 selecting 在你的初始列表上......但这在计算上很糟糕,并且不如...... GroupBy
.
看起来您还想要一些 OrderBy
和 Distinct
子句,Date
您可能是指 DateTime
?否则根据需要应用调整。
var empGroups = emp.GroupBy(e => e.empid);
empGroups.Select(g => new {
empid = g.Key,
min_proj_date = g.Min(e => e.proj_date.Date),
max_proj_date = g.Min(e => e.proj_date.Date),
proj_code = g.Select(e => e.proj_code).OrderBy(pc => pc).Distinct().ToArray()
})
通过第一次分组,您可以对分组的Key
进行操作,以供将来的汇总和selects使用。然后,您 select 一个新的输出类型,并且您在 属性 本身上有第二个内部 select。
请注意 GroupBy
结果的枚举类型是 IGrouping<(string) TKey, (Employee) TElement>
,其中 inherits from IEnumerable<TElement>
。捕获 g
后,它的类型现在是 IEnumerable<Employee>
加上对 g.Key
的访问权限。从那时起,就像对待任何其他 IEnumerable
一样对待它,并使用熟悉的 LINQ。这基本上是实例化变量、循环和添加到列表的替代方法;该变量在中定义并提升到您的 lambda 中。
并不比
复杂多少public class Employee
{
public string EmployeeId ;
public DateTime ProjectDate ;
public string ProjectCode ;
}
public class EmployeeRollup
{
public string EmployeeId ;
public DateTime ProjectDateFrom ;
public DateTime ProjectDateThru ;
public string[] ProjectCodes ;
}
class Program
{
static void Main(string[] args)
{
List<Employee> details = new List<Employee>() ;
List<EmployeeRollup> summary =
details
.GroupBy( e => e.EmployeeId , StringComparer.OrdinalIgnoreCase )
.Select( g => new EmployeeRollup {
EmployeeId = g.Key ,
ProjectDateFrom = g.Min( e => e.ProjectDate ) ,
ProjectDateThru = g.Max( e => e.ProjectDate ) ,
ProjectCodes = g.Select( e => e.ProjectCode )
.Distinct( StringComparer
.OrdinalIgnoreCase )
.ToArray() ,
})
.ToList()
;
}
}
如果您想跟踪每个 Employee
实例在原始列表中的偏移量(位置),您可以这样做:
List<Employee> details = new List<Employee>() ;
int i = 0 ;
List<EmployeeRollup> summary =
details
.Select( e => new KeyValuePair<int,Employee>(i,e) )
.GroupBy( kvp => kvp.Value.EmployeeId , StringComparer.OrdinalIgnoreCase )
...
现在你有一个 KeyValuePair<int,Employee>
的分组,其中每个 KeyValuePair
的 Key
属性 是原始列表中的整数位置及其 Value
属性 是原始 Employee
实例。
只需进行由此产生的更改即可。