Получить сумму двух столбцов в одном запросе LINQ - PullRequest
60 голосов
/ 12 марта 2010

скажем, у меня есть таблица с именем Элементы (ID int, Done int, Total int)

Я могу сделать это двумя запросами:

int total = m.Items.Sum(p=>p.Total)
int done = m.Items.Sum(p=>p.Done)

Но я бы хотел сделать это одним запросом, примерно так:

var x = from p in m.Items select new { Sum(p.Total), Sum(p.Done)};

Конечно, есть способ вызывать агрегатные функции из синтаксиса LINQ ...?

Ответы [ 9 ]

82 голосов
/ 12 марта 2010

Это поможет:

from p in m.Items
group p by 1 into g
select new
{
    SumTotal = g.Sum(x => x.Total), 
    SumDone = g.Sum(x => x.Done) 
};
10 голосов
/ 14 января 2011

Для суммирования таблицы сгруппируйте по константе:

from p in m.Items
group p by 1 into g
select new {
    SumTotal = g.Sum(x => x.Total),
    SumDone = g.Sum(x => x.Done)
}
9 голосов
/ 12 марта 2010

Как насчет

   m.Items.Select(item => new { Total = item.Total, Done = item.Done })
          .Aggregate((t1, t2) => new { Total = t1.Total + t2.Total, Done = t1.Done + t2.Done });
6 голосов
/ 09 февраля 2011

То, что в остальной части моего кода можно извлечь суммы или другие совокупности, смущало меня, пока я не вспомнил, что созданная мною переменная была Iqueryable. Предположим, у нас есть таблица в нашей базе данных, состоящей из заказов, и мы хотим составить сводку для компании ABC:

var myResult = from g in dbcontext.Ordertable
               group p by (p.CUSTNAME == "ABC") into q  // i.e., all of ABC company at once
               select new
{
    tempPrice = q.Sum( x => (x.PRICE ?? 0m) ),  // (?? makes sure we don't get back a nullable)
    tempQty = q.Sum( x => (x.QTY ?? 0m) )
};

Теперь самое интересное - tempPrice и tempQty нигде не объявлены, но они должны быть частью myResult, не так ли? Доступ к ним осуществляется следующим образом:

Console.Writeline(string.Format("You ordered {0} for a total price of {1:C}",
                                 myResult.Single().tempQty,
                                 myResult.Single().tempPrice ));

Также может быть использован ряд других Queryable методов.

3 голосов
/ 12 марта 2010

С помощью класса вспомогательных кортежей, собственного или - в .NET 4 - стандартные, вы можете сделать это:

var init = Tuple.Create(0, 0);

var res = m.Items.Aggregate(init, (t,v) => Tuple.Create(t.Item1 + v.Total, t.Item2 + v.Done));

И res.Item1 - сумма столбца Total и res.Item2 столбца Done.

1 голос
/ 23 августа 2016

На этот вопрос уже был получен ответ, но другие ответы по-прежнему будут выполнять несколько итераций по коллекции (несколько вызовов Sum) или создавать множество промежуточных объектов / кортежей, которые могут быть хорошими, но если это не так, то вы можетесоздайте метод расширения (или несколько), который делает это старомодным способом, но хорошо вписывается в выражение LINQ.

Такой метод расширения будет выглядеть следующим образом:

public static Tuple<int, int> Sum<T>(this IEnumerable<T> collection, Func<T, int> selector1, Func<T, int> selector2)
{
    int a = 0;
    int b = 0;

    foreach(var i in collection)
    {
        a += selector1(i);
        b += selector2(i);
    }

    return Tuple.Create(a, b);
}

И выможно использовать это так:

public class Stuff
{
    public int X;
    public int Y;
}

//...

var stuffs = new List<Stuff>()
{
    new Stuff { X = 1, Y = 10 }, 
    new Stuff { X = 1, Y = 10 }
};

var sums = stuffs.Sum(s => s.X, s => s.Y);
1 голос
/ 02 августа 2012
//Calculate the total in list field values
//Use the header file: 

Using System.Linq;
int i = Total.Sum(G => G.First);

//By using LINQ to calculate the total in a list field,

var T = (from t in Total group t by Total into g select g.Sum(t => t.First)).ToList();

//Here Total is a List and First is the one of the integer field in list(Total)
0 голосов
/ 22 мая 2019

Используя языковую поддержку для кортежей, представленную в C # 7.0 , вы можете решить эту проблему, используя следующее выражение LINQ:

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

Полный пример кода:

var m = new
{
    Items = new[]
    {
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
        new { Total = 10, Done = 1 },
    },
};

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

Console.WriteLine($"Sum of Total: {itemSums.Total}, Sum of Done: {itemSums.Done}");
0 голосов
/ 12 декабря 2013

Когда вы используете group by Linq создает новую коллекцию элементов, поэтому у вас есть две коллекции элементов.

Вот решение обеих проблем:

  1. суммирование любого количества членов за одну итерацию и
  2. избегайте дублирования коллекции вашего предмета

Код:

public static class LinqExtensions
{
  /// <summary>
  /// Computes the sum of the sequence of System.Double values that are obtained 
  /// by invoking one or more transform functions on each element of the input sequence.
  /// </summary>
  /// <param name="source">A sequence of values that are used to calculate a sum.</param>
  /// <param name="selectors">The transform functions to apply to each element.</param>    
  public static double[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double>[] selectors)
  {
    if (selectors.Length == 0)
    {
      return null;
    }
    else
    {
      double[] result = new double[selectors.Length];

      foreach (var item in source)
      {
        for (int i = 0; i < selectors.Length; i++)
        {
          result[i] += selectors[i](item);
        }
      }

      return result;
    }
  }

  /// <summary>
  /// Computes the sum of the sequence of System.Decimal values that are obtained 
  /// by invoking one or more transform functions on each element of the input sequence.
  /// </summary>
  /// <param name="source">A sequence of values that are used to calculate a sum.</param>
  /// <param name="selectors">The transform functions to apply to each element.</param>
  public static double?[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double?>[] selectors) 
  { 
    if (selectors.Length == 0)
    {
      return null;
    }
    else
    {
      double?[] result = new double?[selectors.Length];

      for (int i = 0; i < selectors.Length; i++)
      {
        result[i] = 0;
      }

      foreach (var item in source)
      {
        for (int i = 0; i < selectors.Length; i++)
        {
          double? value = selectors[i](item);

          if (value != null)
          {
            result[i] += value;
          }
        }
      }

      return result;
    }
  }
}

Вот способ суммирования:

double[] result = m.Items.SumMany(p => p.Total, q => q.Done);

Вот общий пример:

struct MyStruct
{
  public double x;
  public double y;
}

MyStruct[] ms = new MyStruct[2];

ms[0] = new MyStruct() { x = 3, y = 5 };
ms[1] = new MyStruct() { x = 4, y = 6 };

// sum both x and y members in one iteration without duplicating the array "ms" by GROUPing it
double[] result = ms.SumMany(a => a.x, b => b.y);

как видите

result[0] = 7 
result[1] = 11
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...