LINQ求和所有属性
本文关键字:属性 求和 LINQ | 更新日期: 2023-09-27 18:30:02
有一种优雅的方法可以做到这一点吗?
假设我处理的对象具有排除性的数字属性(int和doubles),比如:
Foo
-bar1 int
-bar2 int
-bar3 int
-foobar1 double
-foobar2 double
我有一个列表集。。。有没有一种方法可以让它简单地对List中的所有数字属性求和,并返回一个包含所有总数的对象Foo?
一如既往地感谢SO
如果我正确理解了你的问题,并且你想得到一个Foo
实例,其中每个成员是列表中存在的Foo
实例的所有相应成员的总和,那么一种可能的方法(有点冗长,但简单,不涉及魔术)是使用linqAggregate:
// Simplified Foo to prevent a lot of typing.
public class Foo
{
public int bar1 { get; set; }
public int bar2 { get; set; }
public double foobar1 { get; set; }
}
var myFooList = new List<Foo>
{
new Foo() { bar1 = 1, bar2 = 2, foobar1 = 20.0 },
new Foo() { bar1 = 5, bar2 = 8, foobar1 = 42.0 },
new Foo() { bar1 = 9, bar2 = 3, foobar1 = -10.0 },
};
var myFooSum = myFooList.Aggregate(new Foo(), (curSum, foo) =>
{
curSum.bar1 += foo.bar1;
curSum.bar2 += foo.bar2;
curSum.foobar1 += foo.foobar1;
return curSum;
});
Console.WriteLine("FooSum: bar1 = {0}, bar2 = {1}, bar3 = {2}", myFooSum.bar1, myFooSum.bar2, myFooSum.foobar1);
打印:
FooSum: bar1 = 15, bar2 = 13, bar3 = 52
如果我误解了,你想把一个任意的对象列表(其中一些可能包含多个数字字段)求和为一个单一的和值,@Simon Whitehead建议的方法可以奏效:
public class Bar
{
public int baz { get; set; }
public double booz { get; set; }
public string nonNumeric { get; set; }
}
static double SumNumericalProperties(object obj)
{
if (obj == null)
return 0;
if (obj is int)
return (int)obj;
if (obj is double)
return (double)obj;
// etc for other numeric types.
var sum = 0.0;
foreach (var prop in obj.GetType().GetProperties())
{
if (typeof(int).IsAssignableFrom(prop.PropertyType))
sum += (int)prop.GetValue(obj);
else if (typeof(double).IsAssignableFrom(prop.PropertyType))
sum += (double)prop.GetValue(obj);
// etc for other numeric types.
}
return sum;
}
var myObjectList = new List<object>
{
new Foo() { bar1 = 1, bar2 = 2, foobar1 = 20.0 },
new Bar() { baz = 10, booz = 100.0 },
24,
33.3333,
new Foo() { bar1 = 5, bar2 = 8, foobar1 = 42.0 },
new Foo() { bar1 = 9, bar2 = 3, foobar1 = -10.0 },
};
var myFooSum = myObjectList.Sum(SumNumericalProperties);
Console.WriteLine("Sum = {0}", myFooSum);
打印:
Sum = 247.3333
也可以使用以下一行来完成:-)
Item sum = (from p in typeof(Item).GetFields()
where Type.GetTypeCode(p.FieldType) == TypeCode.Int32 || Type.GetTypeCode(p.FieldType) == TypeCode.Double
group p by p into g
select new { Prop=g.Key, Sum=items.Sum(s => (decimal)Convert.ChangeType(g.Key.GetValue(s), TypeCode.Decimal)) })
.Aggregate(new Item(), (state, next) => {
next.Prop.SetValue(state, Convert.ChangeType(next.Sum, next.Prop.FieldType));
return state;
});
完整样品:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
class Item
{
public int bar1 = 0;
public int bar2 = 0;
public double foobar1 = 0;
public double foobar2 = 0;
}
public static void Main ()
{
var items = new List<Item>();
items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
var sum = (from p in typeof(Item).GetFields()
where Type.GetTypeCode(p.FieldType) == TypeCode.Int32 || Type.GetTypeCode(p.FieldType) == TypeCode.Double
group p by p into g
select new { Prop=g.Key, Sum=items.Sum(s => (decimal)Convert.ChangeType(g.Key.GetValue(s), TypeCode.Decimal)) })
.Aggregate(new Item(), (state, next) => {
next.Prop.SetValue(state, Convert.ChangeType(next.Sum, next.Prop.FieldType));
return state;
});
Console.WriteLine("bar1: " + sum.bar1);
Console.WriteLine("bar2: " + sum.bar2);
Console.WriteLine("foobar1: " + sum.foobar1);
Console.WriteLine("foobar2: " + sum.foobar2);
}
}
粗略尝试:
private static double sumNumericalProperties<T>(T obj)
{
var result = 0d;
foreach (var prop in typeof (T).GetProperties())
{
if (typeof(int).IsAssignableFrom(prop.PropertyType))
{
result += (int)prop.GetValue(obj);
}
else if (typeof(double).IsAssignableFrom(prop.PropertyType))
{
result += (double) prop.GetValue(obj);
}
}
return result;
}
示例输入:
class Foo
{
public int Bar1 { get; set; }
public int Bar2 { get; set; }
public double Foobar1 { get; set; }
public double Foobar2 { get; set; }
public string BufferProperty { get; set; }
}
var obj = new Foo() {Bar1 = 2, Bar2 = 4, Foobar1 = 5, Foobar2 = 6};
var obj2 = new Foo() { Bar1 = 2, Bar2 = 4, Foobar1 = 5, Foobar2 = 7 };
var list = new List<Foo>();
list.Add(obj); // 17
list.Add(obj2); // 18
Console.WriteLine(list.Sum(x => sumNumericalProperties(x))); // 35