2

この実装のオブジェクトのリストがあります。

 class Locations
    {
        public string Origin { get; set; }
        public string Dest { get; set; }
        public int Total { get; set; }
    }

行としてオリジン、列としてデスティネーション、および指定されたオリジンとデスティネーションが交わる合計を含むテーブルを CSV 形式で出力する必要があります。リストには、任意の数の Location オブジェクトが存在する可能性があります。

リストに次のレコードがあるとします。

Origin=A
Dest=B
Total=10

Origin=B
Dest=A
Total=20

私の出力は次のようになります (「-」文字は、同一の出発地/目的地間に合計がないことを意味します):

Origins/Destinations,A,B
A,-,10
B,20,-

これまでのところ、次のことを行いました。

1) リストをトラバースして目的地を出力します。

2) リストをトラバースして Origin を出力します。Origin については、リストをトラバースして、Origin に関連する各 Destination の合計を見つけます。

3) 次の Origin に対して #2 を繰り返します。

行/列のデータが常に一致するとは限らないため、結果がうまくいきません。これに対するより簡単な解決策はありますか?

4

4 に答える 4

2

アップデート

Originあなたの更新を読んだ後、与えられたandを持つオブジェクトは1つしかないため、合計する必要はないよう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理論的には、情報を収集しながら を1 回だけ繰り返すことができます。Totalと のfor each ペア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 :)
    }
}
于 2012-05-22T21:03:13.187 に答える
1

あなたの問題は2次元マトリックス構造(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 メソッドを変更してコンテンツを表示できると思います。

[更新] ラベルを印刷するようにコードを更新しました。このコードはテストされていないため、問題がある場合とない場合があります。

于 2012-05-22T21:44:50.377 に答える
1

あなたの要件を正しく理解しているかどうかわかりませんが、これは役に立ちますか?

var destinationGroups = locations
    .GroupBy(l=> l.Dest)
    .Select(grp => new{
        SumTotal = grp.Sum(l => l.Total),
        Destination = grp.Key ,
        CountOrigins = grp.Count()
    });
于 2012-05-22T21:02:59.150 に答える
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;
于 2012-05-22T21:27:00.500 に答える