LINQ Lambda求和NULL

时间:2014-12-12 16:20:55

标签: c# linq lambda

我正在使用LINQ to对象来对两个对象中的值求和,并返回该对象的单个版本以及总和。

我遇到的问题是LINQ求和函数将NULL求和为零(0)。我希望如果我有一个值'#34; 15"而另一个值为" null",总和应为" 15"。但我希望如果第一个值是" null"第二个值是" null",总和也应该是" null"。但是,它告诉我,总和是" 0"。

我怎样才能让它像我期望的那样发挥作用?如果至少有一个值或返回" null"我希望它通过返回一个值来表现。如果没有价值观。

现在有些代码:

virtual public IStatSplit Totals
{
  get
  {
    var cSplit = _splits.Where(s => s.Split == SplitType.COMBINED).SingleOrDefault();

    if( cSplit != null )
    { return cSplit; }

    cSplit = _splits.Where( s => s.Split != SplitType.COMBINED )
                    .GroupBy( g => 1 == 1 ).Select( x => new StatSplit
    {
      AB = (uint?)x.Sum( q => q.AB ),
      CI = (uint?)x.Sum( q => q.CI ),
      B2 = (uint?)x.Sum( q => q.B2 ),
      B3 = (uint?)x.Sum( q => q.B3 ),
      GDP = (uint?)x.Sum( q => q.GDP ),
      H = (uint?)x.Sum( q => q.H ),
      HB = (uint?)x.Sum( q => q.HB ),
      HR = (uint?)x.Sum( q => q.HR ),
      RBI = (uint?)x.Sum( q => q.RBI ),
      IBB = (uint?)x.Sum( q => q.IBB ),
      SF = (uint?)x.Sum( q => q.SF ),
      SH = (uint?)x.Sum( q => q.SH ),
      SO = (uint?)x.Sum( q => q.SO ),
      BB = (uint?)x.Sum( q => q.BB ),
      Split = SplitType.COMBINED
    } ).SingleOrDefault();

    return cSplit;
  }
}

以下是不通过单元测试的测试数据:

[TestMethod]
public void PitchingTotals()
{
  var splits = GetSplits();
  var pitching = new Base.Pitching();
  pitching.Splits = splits;

  var expected = GetTotalSplit();
  var result = pitching.Totals;

  // result.RBI = 0
  // expected.RBI = null
  // this fails because the "0" is not expected

  Assert.AreEqual( expected, result );
}

private List<IStatSplit> GetSplits()
{
  var lhSplit = new Base.StatSplit
  {
    AB = 442,
    H = 97,
    B2 = 14,
    B3 = 0,
    HR = 6,
    BB = 28,
    HB = 6,
    SF = 1,
    SH = 5,
    SO = 73,
    GDP = 7,
    IBB = 4,
    CI = 0,
    RBI = null,
    Split = Enumerations.SplitType.VS_LEFT
  };

  var rhSplit = new Base.StatSplit
  {
    AB = 633,
    H = 101,
    B2 = 9,
    B3 = 0,
    HR = 5,
    BB = 34,
    HB = 1,
    SF = 1,
    SH = 10,
    SO = 195,
    GDP = 11,
    IBB = 2,
    CI = 0,
    RBI = null,
    Split = Enumerations.SplitType.VS_RIGHT
  };

  List<IStatSplit> splits = new List<IStatSplit>();
  splits.Add( lhSplit );
  splits.Add( rhSplit );

  return splits;
}

private IStatSplit GetTotalSplit()
{
  var split = new Base.StatSplit
  {
    AB = 1075,
    H = 198,
    B2 = 23,
    B3 = 0,
    HR = 11,
    BB = 62,
    HB = 7,
    SF = 2,
    SH = 15,
    SO = 268,
    GDP = 18,
    IBB = 6,
    CI = 0,
    RBI = null,
    Split = Enumerations.SplitType.COMBINED
  };

  return split;
}

3 个答案:

答案 0 :(得分:2)

您可以使用Aggregate

而不是Sum
x.Aggregate(
    (uint?)null,
    (sum, currentItem) => 
        !sum.HasValue && !currentItem.AB.HasValue ? 
        (uint?)null : 
        sum.GetValueOrDefault() + currentItem.AB.GetValueOrDefault());

这将以null uint?值开头,并遍历每个项目。如果当前sum和值currentItem.AB都是null,那么下一个sum将继续为null。如果其中任何一个不是null,则会添加它们,如果nulluint,则使用默认值,{{1}}为0。

答案 1 :(得分:0)

你应该写自己的&#34; SumOrNull&#34;可以返回&#34; uint的扩展方法?&#34;在你描述的情况下(null + null = null)。现有的Linq Sum方法只返回一个不可为空的数字。

Linq Sum documentation

答案 2 :(得分:0)

根据Juharr的回应,我首先尝试了这个:

cSplit = _splits.Where( s => s.Split != SplitType.COMBINED )
    .GroupBy( g => 1 == 1 ).Select( x => new StatSplit
    {
      AB = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.AB.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.AB.GetValueOrDefault() ),
      CI = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.CI.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.CI.GetValueOrDefault() ),
      B2 = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.B2.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.B2.GetValueOrDefault() ),
      B3 = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.B3.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.B3.GetValueOrDefault() ),
      GDP = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.GDP.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.GDP.GetValueOrDefault() ),
      H = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.H.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.H.GetValueOrDefault() ),
      HB = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.HB.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.HB.GetValueOrDefault() ),
      HR = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.HR.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.HR.GetValueOrDefault() ),
      RBI = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.RBI.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.RBI.GetValueOrDefault() ),
      IBB = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.IBB.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.IBB.GetValueOrDefault() ),
      SF = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.SF.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.SF.GetValueOrDefault() ),
      SH = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.SH.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.SH.GetValueOrDefault() ),
      SO = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.SO.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.SO.GetValueOrDefault() ),
      BB = x.Aggregate( (uint?)null, ( sum, item ) => !sum.HasValue && !item.BB.HasValue ?
            (uint?)null : sum.GetValueOrDefault() + item.BB.GetValueOrDefault() ),
      Split = SplitType.COMBINED
    } ).SingleOrDefault();

这很有效,但对我来说看起来很难看。我做了一点精炼,想出了这个:

cSplit = _splits.Aggregate( new StatSplit() { Split = SplitType.COMBINED },
    ( sum, item ) =>
    {
      sum.AB = !sum.AB.HasValue && !item.AB.HasValue ? (uint?)null : sum.AB.GetValueOrDefault() + item.AB.GetValueOrDefault();
      sum.CI = !sum.CI.HasValue && !item.CI.HasValue ? (uint?)null : sum.CI.GetValueOrDefault() + item.CI.GetValueOrDefault();
      sum.B2 = !sum.B2.HasValue && !item.B2.HasValue ? (uint?)null : sum.B2.GetValueOrDefault() + item.B2.GetValueOrDefault();
      sum.B3 = !sum.B3.HasValue && !item.B3.HasValue ? (uint?)null : sum.B3.GetValueOrDefault() + item.B3.GetValueOrDefault();
      sum.GDP = !sum.GDP.HasValue && !item.GDP.HasValue ? (uint?)null : sum.GDP.GetValueOrDefault() + item.GDP.GetValueOrDefault();
      sum.H = !sum.H.HasValue && !item.H.HasValue ? (uint?)null : sum.H.GetValueOrDefault() + item.H.GetValueOrDefault();
      sum.HB = !sum.HB.HasValue && !item.HB.HasValue ? (uint?)null : sum.HB.GetValueOrDefault() + item.HB.GetValueOrDefault();
      sum.HR = !sum.HR.HasValue && !item.HR.HasValue ? (uint?)null : sum.HR.GetValueOrDefault() + item.HR.GetValueOrDefault();
      sum.RBI = !sum.RBI.HasValue && !item.RBI.HasValue ? (uint?)null : sum.RBI.GetValueOrDefault() + item.RBI.GetValueOrDefault();
      sum.IBB = !sum.IBB.HasValue && !item.IBB.HasValue ? (uint?)null : sum.IBB.GetValueOrDefault() + item.IBB.GetValueOrDefault();
      sum.SF = !sum.SF.HasValue && !item.SF.HasValue ? (uint?)null : sum.SF.GetValueOrDefault() + item.SF.GetValueOrDefault();
      sum.SH = !sum.SH.HasValue && !item.SH.HasValue ? (uint?)null : sum.SH.GetValueOrDefault() + item.SH.GetValueOrDefault();
      sum.SO = !sum.SO.HasValue && !item.SO.HasValue ? (uint?)null : sum.SO.GetValueOrDefault() + item.SO.GetValueOrDefault();
      sum.BB = !sum.BB.HasValue && !item.BB.HasValue ? (uint?)null : sum.BB.GetValueOrDefault() + item.BB.GetValueOrDefault();
      return sum;
    } );

第一个在大约12毫秒内运行,列表中只有两个项目。精致的第二个在大约9毫秒内运行,列表中只有两个项目。我将使用第二个条目,因为它更高效,也看起来更干净,更容易遵循。感谢Juharr推动正确的方向!