2010-03-12 7 views
48

chúng ta hãy nói rằng tôi có một bảng gọi là Items (ID int, Done int, Tổng int)Nhận tổng của hai cột trong một truy vấn LINQ

tôi có thể làm điều đó bởi hai truy vấn:

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

Nhưng tôi muốn làm điều đó trong một truy vấn, một cái gì đó như thế này:

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

Chắc chắn có một cách để gọi chức năng tổng hợp từ cú pháp LINQ ...?

Trả lời

68

này sẽ làm các trick:

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) 
}; 
+7

Hoặc khi hàng không có định danh duy nhất, bạn có thể viết 'nhóm p bởi p vào g'. – Steven

+1

Điều đó đã làm các trick, mặc dù với một sửa đổi: từ p trong m.Items nhóm p bởi p.Id vào g chọn new {SumTotal = g.Sum (r => r.Total), SumDone = g.Sum (r => r.Done)} – Axarydax

+1

Bạn đã đúng, 'p' đã được sử dụng trong truy vấn. Sửa lỗi. – Steven

1

Với một lớp helper tuple, hoặc của riêng bạn hoặc — trong .NET 4 — những tiêu chuẩn mà bạn có thể làm điều này:

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 là tổng của cột Totalres.Item2 của cột Done .

8

Làm thế nào về

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 }); 
9

Tổng hợp bảng, nhóm bởi một hằng số:

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) 
} 
+0

xin vui lòng ai đó có thể đăng một phiên bản vb.net? – Zeus

+0

@Zeus Đây là Mã VB.Net, Từ g Trong Từ p Trong m.ItemsGroup p Bằng 1New Với {_ \t Khóa .SumTotal = g.Sum (Hàm (x) x.Total), _ \t Key. SumDone = g.Sum (Chức năng (x) x.Done) _ } Bạn có thể chuyển đổi mã tại đây [link] (http://converter.telerik.com/) –

+0

@Sibeesh, cảm ơn sự giúp đỡ – Zeus

5

Tìm ra nơi để trích xuất các khoản tiền hoặc tổng hợp khác trong phần còn lại của mã của tôi nhầm lẫn tôi, cho đến khi tôi nhớ rằng biến tôi xây dựng là một Iqueryable. Giả sử chúng ta có một bảng trong cơ sở dữ liệu của chúng tôi bao gồm đơn đặt hàng, và chúng tôi muốn tạo ra một bản tóm tắt cho công ty 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)) 
}; 

Bây giờ phần thú vị - tempPrice và tempQty không tuyên bố bất cứ nơi nào mà họ phải là một phần của myResult, phải không? Truy cập chúng như sau:

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

Một số phương pháp có thể truy vấn khác cũng có thể được sử dụng.

1
//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) 
-2

Khi bạn sử dụng nhóm bởi LINQ tạo ra một bộ sưu tập mới của các mặt hàng, do đó bạn có hai bộ sưu tập của các mặt hàng.

Dưới đây là một giải pháp cho cả hai vấn đề:

  1. tổng hợp bất kỳ số lượng các thành viên trong một lần lặp và
  2. tránh sao chép bộ sưu tập của hàng của bạn

Code:

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; 
    } 
    } 
} 

Dưới đây là cách bạn phải thực hiện tổng kết:

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

Dưới đây là một ví dụ tổng quát:

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); 

như bạn có thể nhìn thấy

result[0] = 7 
result[1] = 11 
1

này đã được trả lời đã có, nhưng câu trả lời khác vẫn sẽ làm nhiều lần lặp trong bộ sưu tập (nhiều cuộc gọi Sum) hoặc tạo ra rất nhiều đối tượng trung gian/Tuples có thể tốt, nhưng nếu không, thì bạn có thể tạo một phương thức mở rộng (hoặc nhiều) làm theo cách cũ nhưng phù hợp với biểu thức LINQ.

một phương pháp mở rộng như vậy sẽ giống như thế này:

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); 
} 

Và bạn có thể sử dụng nó như thế này:

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);