C# 将对象列表转换为表格结构
我有一个包含此实现的对象列表:C# 将对象列表转换为表格结构,c#,.net,linq,data-structures,C#,.net,Linq,Data Structures,我有一个包含此实现的对象列表: class Locations { public string Origin { get; set; } public string Dest { get; set; } public int Total { get; set; } } 我需要以CSV格式输出一个表,该表以行的形式显示起点,以列的形式显示目的地,以及指定的起点和目的地的合计。我的列表中可能有任意数量的位置对象 鉴于我名单中的以下记
class Locations
{
public string Origin { get; set; }
public string Dest { get; set; }
public int Total { get; set; }
}
我需要以CSV格式输出一个表,该表以行的形式显示起点,以列的形式显示目的地,以及指定的起点和目的地的合计。我的列表中可能有任意数量的位置对象
鉴于我名单中的以下记录:
Origin=A
Dest=B
Total=10
Origin=B
Dest=A
Total=20
我的输出如下所示(带“-”字符表示相同的起点/目的地之间没有总计):
到目前为止,我已经做了以下工作:
1) 遍历列表以输出目的地
2) 遍历列表以输出原点。对于原点,遍历列表以查找与原点相关的每个目标的总数
3) 对下一个原点重复#2
由于行/列数据并不总是匹配,因此我的结果不太合适。有更简单的解决方案吗?我不确定是否正确理解了您的要求,但这有帮助吗
var destinationGroups = locations
.GroupBy(l=> l.Dest)
.Select(grp => new{
SumTotal = grp.Sum(l => l.Total),
Destination = grp.Key ,
CountOrigins = grp.Count()
});
我不确定我是否正确理解了您的要求,但这有帮助吗
var destinationGroups = locations
.GroupBy(l=> l.Dest)
.Select(grp => new{
SumTotal = grp.Sum(l => l.Total),
Destination = grp.Key ,
CountOrigins = grp.Count()
});
使现代化
在阅读您的更新之后,似乎没有必要求和,因为只有一个对象具有给定的原点
和目标
。在这种情况下,您可以保留收集位置的代码,执行嵌套的foreach
,只需使用
var item = locations.FirstOrDefault(l => l.Origin == origin && l.Dest == dest);
var total = item == null ? 0 : item.Total;
原始答案
您可以使用获取所有位置的列表
var locations = new List<Locations>(); // assume it's populated
var places = locations.Select(l => l.Origin)
.Concat(locations.Select(l => l.Dest))
.Distinct()
.OrderBy(s => s); // why not sort it as well
您可以立即看到如何使用此结构轻松构造表。这种方法的主要缺点是它所做的工作比严格必要的要多得多;理论上,我们当然可以在位置上迭代一次,一边收集信息。对于每对Origin
和Dest
,可以使用
var totals = locations.GroupBy(l => new { l.Origin, l.Dest })
.ToDictionary(g => Tuple.Create(g.Key.Origin, g.Key.Dest),
g => g.Sum(r => r.Total));
此时,我们可以从第一个解决方案中选取一页:
foreach (var origin in places)
{
foreach (var dest in places)
{
var total = totals[Tuple.Create(origin, dest)]; // almost too easy :)
}
}
使现代化
在阅读您的更新之后,似乎没有必要求和,因为只有一个对象具有给定的原点
和目标
。在这种情况下,您可以保留收集位置的代码,执行嵌套的foreach
,只需使用
var item = locations.FirstOrDefault(l => l.Origin == origin && l.Dest == dest);
var total = item == null ? 0 : item.Total;
原始答案
您可以使用获取所有位置的列表
var locations = new List<Locations>(); // assume it's populated
var places = locations.Select(l => l.Origin)
.Concat(locations.Select(l => l.Dest))
.Distinct()
.OrderBy(s => s); // why not sort it as well
您可以立即看到如何使用此结构轻松构造表。这种方法的主要缺点是它所做的工作比严格必要的要多得多;理论上,我们当然可以在位置上迭代一次,一边收集信息。对于每对Origin
和Dest
,可以使用
var totals = locations.GroupBy(l => new { l.Origin, l.Dest })
.ToDictionary(g => Tuple.Create(g.Key.Origin, g.Key.Dest),
g => g.Sum(r => r.Total));
此时,我们可以从第一个解决方案中选取一页:
foreach (var origin in places)
{
foreach (var dest in places)
{
var total = totals[Tuple.Create(origin, dest)]; // almost too easy :)
}
}
您的问题似乎与二维矩阵结构(它被称为O/D矩阵)有关,但重要的是不要限制大小。所以,我建议您使用集合构建矩阵结构,以允许不限制大小。我将代码片段放在下面,以演示如何操作。然而,这段代码可能不是最优的,但您可能会得到一些启发
公共类位置
{
公共字符串源{get;set;}
公共字符串Dest{get;set;}
公共布尔等于(其他位置)
{
//...
}
公共覆盖布尔等于(对象对象对象)
{
//...
}
公共覆盖int GetHashCode()
{
//...
}
}
公共类ODM矩阵
{
私有只读哈希集_origins=new HashSet();
私有只读哈希集_dests=new HashSet();
私有只读字典_值=新字典();
公共int此[位置]
{
收到
{
如果(!_值.ContainsKey(位置))
{
设置值(位置,0);
}
返回_值[位置];
}
set{SetValue(位置,值);}
}
私有void SetValue(位置,int值)
{
if(!\u origins.Contains(location.Origin))
_origins.Add(location.Origin);
如果(!\u目的地包含(location.Dest))
_目的地添加(地点目的地);
_值[位置]=值;
}
public int this[字符串原点,字符串目标]
{
获取{返回此[新位置{Origin=Origin,Dest=Dest}];}
设置{this[新位置{Origin=Origin,Dest=Dest}]=value;}
}
公共重写字符串ToString()
{
var content=新的StringBuilder();
//打印目的地标签
内容附录(_dest.Aggregate((x,y)=>x+”,“+y));
foreach(字符串原点在_原点中)
{
//打印原产地标签
内容。追加(来源+“,”);
foreach(字符串dest in_dests)
{
追加(此[来源,目的地]+“,”);
}
content.Remove(content.Length-2,2);
content.AppendLine();
}
返回content.ToString();
}
}
哦,从你最新的问题中,我查到你想要打印原始和目的地的标签。我认为您可以修改ToString方法来显示内容
[更新]
我更新了代码以打印标签。此代码未经测试,因此可能会有问题,也可能不会有问题。您的问题似乎与二维矩阵结构有关(称为O/D矩阵),但重要的是不要限制大小。所以,我建议您使用集合构建矩阵结构,以允许不限制大小。我将代码片段放在下面,以演示如何操作。然而,这段代码可能不是最优的,但您可能会得到一些启发
公共类位置
{
公共字符串源{get;set;}
公共字符串Dest{get;set;}
公共布尔等于(其他位置)
{
//...
}
公共覆盖布尔等于(对象对象对象)