从linq C#中的对象列表中获取总和

时间:2013-12-16 11:53:42

标签: linq c#-4.0

我有如下所述的对象列表:

List<Maths> mObjs = new List<Maths>();
mObjs.Add(new Maths{ Name = "Jack", M1 = 10, M2 = 5, M3 = 0, M4 = 2, M5 =1 });
mObjs.Add(new Maths { Name = "Jill", M1 = 2, M2 = 3, M3 = 4, M4 = 1, M5 = 0 });
mObjs.Add(new Maths { Name = "Michel", M1 = 12, M2 = 15, M3 = 10, M4 = 12, M5 = 11 });

现在我需要计算所有三个人的总聚合值。 我需要得到以下结果,可能是一个新的其他类

List<Results> mRes = new List<Results>();

public class Results{
    public string Name { get; set; }
    public int TotalValue { get; set; }

}

mRes.Name = "M1"
mRes.TotalValue  = 24;

mRes.Name = "M2"
mRes.TotalValue  = 23;

mRes.Name = "M3"
mRes.TotalValue  = 14;

mRes.Name = "M4"
mRes.TotalValue  = 15;


mRes.Name = "M5"
mRes.TotalValue  = 12;

如何使用linq查询从mObjs获取此数据?我知道我们可以使用for来做,但想知道是否有更好的方法来使用linq查询来获得这个,因为这减少了代码行,我在许多其他地方有类似的要求,并且不想写foreach或fors的数量每一次。

3 个答案:

答案 0 :(得分:4)

您可以使用预选列表列出要选择的名称和字段

var lookups = new Dictionary<string,Func<Maths,int>> {
    {"M1", x => x.M1 },
    {"M2", x => x.M2 },
    {"M3", x => x.M3 },
    {"M4", x => x.M4 },
    {"M5", x => x.M5 },
};

然后你可以简单地做

var mRes = dlookups.Select(x => new Results { 
                                              Name= x.Key,
                                              TotalValue = mObjs.Sum(x.Value)
                           }).ToList();

开始更新*

回应评论

lambda表达式只是从源类到int的函数。

例如

class Sub1 {
  string M3 {get;set;}
  int M4 {get;set;}
}

class Math2 {
  string Name {get;set;}
  string M1 {get;set;}
  string M2 {get;set;}
  Sub1 Sub {get;set;}
}

var lookups = new Dictionary<string,Func<Math2,int>> {
   { "M1", x => int.Parse(x.M1) },
   { "M2", x => int.Parse(x.M2) },
   { "M3", x => int.Parse(x.Sub.M3) },
   { "M4", x => int.Parse(x.Sub.M4} }
};

或者,如果您想要进行一些错误检查,您可以使用函数或嵌入代码。

int GetInt(string source) {
  if (source == null) return 0;
  int result;
  return int.TryParse(source, out result) ? result : 0;
}

var lookups = new Dictionary<string,Func<Math2,int>> {
   { "M1", x => {
                  int result;
                  return x == null ? 0 : (int.TryParse(x,out result) ? result : 0);
                },
   { "M2", x => GetInt(x) },
   { "M3", x => x.Sub == null ? 0 : GetInt(x.Sub.M3) },
   { "M4", x => x.Sub == null ? 0 : x.Sub.M4}
};

END UPDATED

如果你想更进一步,你可以使用反射来构建查找词典。

这是一个辅助函数,它将为类的所有Integer属性生成查找。

public Dictionary<string,Func<T,int>> GenerateLookups<T>() where T: class {
    // This just looks for int properties, you could add your own filter
var properties = typeof(T).GetProperties().Where(pi => pi.PropertyType == typeof(int));

var parameter = Expression.Parameter(typeof(T));

  return properties.Select(x => new { 
     Key = x.Name, 
     Value = Expression.Lambda<Func<T,int>>(Expression.Property(parameter,x),parameter).Compile()
  }).ToDictionary (x => x.Key, x => x.Value);

}

现在你可以这样做:

 var mRes=GenerateLookups<Maths>().Select( x => new Results
   {
     Name = x.Key,
     TotalValue = mObjs.Sum(x.Value)
   }).ToList();

答案 1 :(得分:1)

不是很聪明但有效且可读:

int m1Total= 0;
int m2Total= 0;
int m3Total= 0;
int m4Total= 0;
int m5Total= 0;
foreach(Maths m in mObjs)
{
    m1Total += m.M1;
    m2Total += m.M2;
    m3Total += m.M3;
    m4Total += m.M4;
    m5Total += m.M5;
}
List<Results> mRes = new List<Results>
{
    new Results{ Name = "M1", TotalValue = m1Total },
    new Results{ Name = "M2", TotalValue = m2Total },
    new Results{ Name = "M3", TotalValue = m3Total },
    new Results{ Name = "M4", TotalValue = m4Total },
    new Results{ Name = "M5", TotalValue = m5Total },
}; 

结果:

Name: "M1"  TotalValue: 24  
Name: "M2"  TotalValue: 23  
Name: "M3"  TotalValue: 14  
Name: "M4"  TotalValue: 15  
Name: "M5"  TotalValue: 12  

编辑:既然您明确要求LINQ,如果属性总是这五个,我不明白为什么你需要使用LINQ。如果数字可以改变,我将使用不同的结构。

您可以使用

  • 单个List<Measurement>而不是多个属性,其中Measurement是另一个存储名称和值的类,或者您可以使用
  • 一个Dictionary<string, int>,用于高效查找。

答案 2 :(得分:0)

你可以试试这样的事情:

mRes.Add(new Results() { Name = "M1", TotalValue = mObjs.Sum(x => x.M1) });

要以编程方式迭代所有类属性,您可能需要使用反射。