我有一个具有此实现的对象列表:
class Locations
{
public string Origin { get; set; }
public string Dest { get; set; }
public int Total { get; set; }
}
我需要以CSV格式输出一个表格,其中Origins为行,Destinations为列,以及指定的Origins和Destinations相遇的Total。我的列表中可能有任意数量的Location对象。
鉴于我的名单中有以下记录:
Origin=A
Dest=B
Total=10
Origin=B
Dest=A
Total=20
我的输出看起来像这样(' - '字符表示相同的Origin / Destinations之间没有Total):
Origins/Destinations,A,B
A,-,10
B,20,-
到目前为止,我已经做了以下事情:
1)遍历列表以输出目的地。
2)遍历列表以输出Origin。对于Origin,遍历列表以查找与Origin相关的每个Destination的Total。
3)对下一个Origin重复#2。
由于行/列数据并不总是匹配,我的结果不太合适。有没有更简单的解决方案呢?
答案 0 :(得分:2)
阅读完更新后,似乎无需求和,因为只有一个对象具有给定的Origin
和Destination
。在这种情况下,您可以保留收集places
的代码,执行嵌套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
此时,您可以简单地遍历places
行,并为列执行另一次嵌套迭代:
foreach (var origin in places)
{
foreach (var dest in places)
{
var total = locations.Where(l => l.Origin == origin && l.Dest == dest)
.Sum(l => l.Total);
}
}
您可以立即看到如何使用此结构轻松构建表。这种方法的主要缺点是它做的工作比严格必要的要多得多;从理论上讲,我们当然可以在locations
上迭代一次,收集信息。可以将每对Total
和Origin
的{{1}}与
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));
答案 1 :(得分:1)
我不确定我是否理解你的要求,但这有帮助吗?
var destinationGroups = locations
.GroupBy(l=> l.Dest)
.Select(grp => new{
SumTotal = grp.Sum(l => l.Total),
Destination = grp.Key ,
CountOrigins = grp.Count()
});
答案 2 :(得分:1)
您的问题似乎与二维矩阵结构有关(它被称为 O / D矩阵),但重要的是不要限制大小。因此,我建议您使用集合构建矩阵结构,以允许不限制大小。我将代码片段如下所示,以显示如何操作。但是,这段代码可能不是最佳的,但你可以得到一些亮点。
public class Location
{
public string Origin { get; set; }
public string Dest { get; set; }
public bool Equals(Location other)
{
//...
}
public override bool Equals(object obj)
{
//...
}
public override int GetHashCode()
{
//...
}
}
public class ODMatrix
{
private readonly HashSet<string> _origins = new HashSet<string>();
private readonly HashSet<string> _dests = new HashSet<string>();
private readonly Dictionary<Location, int> _values = new Dictionary<Location, int>();
public int this[Location location]
{
get
{
if (!_values.ContainsKey(location))
{
SetValue(location, 0);
}
return _values[location];
}
set { SetValue(location, value); }
}
private void SetValue(Location location, int value)
{
if (!_origins.Contains(location.Origin))
_origins.Add(location.Origin);
if (!_dests.Contains(location.Dest))
_dests.Add(location.Dest);
_values[location] = value;
}
public int this[string origin, string dest]
{
get { return this[new Location {Origin = origin, Dest = dest}]; }
set { this[new Location {Origin = origin, Dest = dest}] = value; }
}
public override string ToString()
{
var content = new StringBuilder();
//print dest lables
content.AppendLine(_dests.Aggregate((x, y) => x + ", " + y));
foreach (string origin in _origins)
{
//print origin lable
content.Append(origin + ", ");
foreach (string dest in _dests)
{
content.Append(this[origin, dest] + ", ");
}
content.Remove(content.Length - 2, 2);
content.AppendLine();
}
return content.ToString();
}
}
哦,从您更新的问题,我检查了您要打印原点和目标的标签。我想你可以修改ToString方法来显示内容。
<强> [增订] 强> 我更新了代码以打印标签。此代码未经过测试,因此可能会或可能不会有任何问题。
答案 3 :(得分:0)
// Group locations by origin
var table =
from location in list
group location by location.Origin into originGroups
select new {
Origin = originGroups.Key
Destinations = originGroups.ToDictionary(x => x.Dest)
};
// Get list of destinations
var destinations = list.Select(location => location.Dest).Distinct();
// Output everything
foreach(var location in table)
foreach(var dest in destinations)
// output location.Destinations[dest].Total;