我有一个类型Element
的通用列表,例如。
public class Element
{
public string Country { get; set; }
public string City { get; set; }
public int Population { get; set; }
}
使用以下数据。
var elements = new List<Element>
{
new Element { Country = "Country A", City = "Barrie", Population = 12 },
new Element { Country = "Country A", City = "Barrie2", Population = 12 },
new Element { Country = "Country A", City = "Barrie2", Population = 12 },
new Element { Country = "Country A", City = "Barrie", Population = 12 },
new Element { Country = "Country D", City = "Essex", Population = 12 },
new Element { Country = "Country A", City = "Barrie", Population = 12 },
new Element { Country = "Country A", City = "Barrie", Population = 12 },
new Element { Country = "Country D", City = "Essex", Population = 12 },
new Element { Country = "Country A", City = "Barrie", Population = 12 },
new Element { Country = "Country A", City = "Barrie", Population = 12 }
};
基本上,我想按国家和城市分组的人口总数。
类似的东西。
Country A | Barrie | `running total for Barrie`
Country A | Barrie2 | `running total for Barrie2`
| | `total for Country A`
Country D | Essex | `running total for Essex`
| | `total for Country D`
| | `total for everything`
我无法在任何地方找到一个扩展名(我说扩展名,因为我计划多次使用汇总)所以我想我会试一试。所以我从这个简单的查询开始。
var groupedElements = elements
.GroupBy(x => new { x.Country, x.City })
.Select(x => new { Country = x.Key, City = x.Select(xx => xx.City), Population = x.Sum(xx => xx.Population) })
.ToList();
此查询按预期工作,所以我认为我走在正确的轨道上。接下来我想我必须弄清楚groupedElements
中哪个属性是聚合的,因为这就是我们将要进行汇总的内容。我该如何做到这一点?或许我可以使用一个参数来指定我希望在哪个列上执行聚合函数。
答案 0 :(得分:2)
我认为这并不像你想象的那么容易。首先,您想要的结果序列的项目不是“自然”的相同类型。您希望在每个国家/地区内运行总计,为每个国家/地区分组总计,然后整体总计。
即使您可以编写简洁的查询来创建此信息,调用者也必须区分每种类型的总和与结果。我想知道在一开始就将你想要的结果作为一个IEnumerable<XXX>
来看是否有意义。创建一个像这样的优秀OO解决方案并从那里开始可能会更有意义:
public interface IGeographicEnity
{
string Name { get; }
int Population { get; }
}
public class City : IGeographicEntity {...}
public class Country : IGeographicEntity
{
public IList<City> Cities { get {...} }
public int Population { get { return Cities.Sum(c => c.Population); } }
...
}
public class World : IGeographicEntity
{
public IList<Country> Countries { get {...} }
public int Population { get { return Countries.Sum(c => c.Population); }
...
}
如果你仍然想坚持原来的想法,那么这是我能想到的最好的:
public class PopulationTotal
{
// You can use subclassing instead of an enum to represent this
public enum Kind
{ RunningTotalWithinCountry, TotalForCountry, TotalOverall }
public string GroupName { get; private set; }
public int Value { get; private set; }
public Kind Kind { get; private set; }
public static IEnumerable<PopulationTotal> GetTotals(IEnumerable<Element> elements)
{
int overallTotal = 0;
foreach (var elementsByCountry in elements.GroupBy(e => e.Country))
{
int runningTotalForCountry = 0;
foreach (var element in elementsByCountry)
{
runningTotalForCountry += element.Population;
yield return new PopulationTotal
{
GroupName = element.City,
Kind = Kind.RunningTotalWithinCountry,
Value = runningTotalForCountry
};
}
overallTotal += runningTotalForCountry;
yield return new PopulationTotal
{
GroupName = elementsByCountry.Key,
Kind = Kind.TotalForCountry,
Value = runningTotalForCountry
};
}
yield return new PopulationTotal
{
GroupName = null,
Kind = Kind.TotalOverall,
Value = overallTotal
};
}
}
<强>用法强>:
var totals = PopulationTotal.GetTotals(elements);