将对象列表转换为表格结构

时间:2012-05-22 20:55:52

标签: c# .net linq data-structures

我有一个具有此实现的对象列表:

 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。

由于行/列数据并不总是匹配,我的结果不太合适。有没有更简单的解决方案呢?

4 个答案:

答案 0 :(得分:2)

更新

阅读完更新后,似乎无需求和,因为只有一个对象具有给定的OriginDestination。在这种情况下,您可以保留收集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上迭代一次,收集信息。可以将每对TotalOrigin的{​​{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;