Linq: GroupBy, Sum और Count


133

मेरे पास उत्पादों का एक संग्रह है

public class Product {

   public Product() { }

   public string ProductCode {get; set;}
   public decimal Price {get; set; }
   public string Name {get; set;}
}

अब मैं उत्पाद कोड के आधार पर संग्रह को समूहीकृत करना चाहता हूं और प्रत्येक कोड के लिए नाम, संख्या या उत्पादों के साथ एक वस्तु लौटाता हूं और प्रत्येक उत्पाद के लिए कुल मूल्य।

public class ResultLine{

   public ResultLine() { }

   public string ProductName {get; set;}
   public string Price {get; set; }
   public string Quantity {get; set;}
}

इसलिए मैं ProductCode द्वारा समूह के लिए GroupBy का उपयोग करता हूं, फिर मैं राशि की गणना करता हूं और प्रत्येक उत्पाद कोड के रिकॉर्ड की संख्या भी गिनता हूं।

अभी तक मेरे पास इतना ही है:

List<Product> Lines = LoadProducts();    
List<ResultLine> result = Lines
                .GroupBy(l => l.ProductCode)
                .SelectMany(cl => cl.Select(
                    csLine => new ResultLine
                    {
                        ProductName =csLine.Name,
                        Quantity = cl.Count().ToString(),
                        Price = cl.Sum(c => c.Price).ToString(),
                    })).ToList<ResultLine>();

किसी कारण से, योग सही ढंग से किया जाता है लेकिन गिनती हमेशा 1 होती है।

Sampe डेटा:

List<CartLine> Lines = new List<CartLine>();
            Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
            Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
            Lines.Add(new CartLine() { ProductCode = "p2", Price = 12M, Name = "Product2" });

नमूना डेटा के साथ परिणाम:

Product1: count 1   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

उत्पाद 1 की गिनती = 2 होनी चाहिए!

मैंने इसे एक साधारण कंसोल एप्लिकेशन में अनुकरण करने की कोशिश की, लेकिन मुझे निम्नलिखित परिणाम मिले:

Product1: count 2   - Price:13 (2x6.5)
Product1: count 2   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

Product1: केवल एक बार सूचीबद्ध किया जाना चाहिए ... उपरोक्त कोड पास्टबिन पर पाया जा सकता है: http://pastebin.com/cHTVie

जवाबों:


285

मुझे समझ नहीं आ रहा है कि पहला "नमूना डेटा के साथ परिणाम" कहां से आ रहा है, लेकिन कंसोल ऐप में समस्या यह है कि आप प्रत्येक समूह में प्रत्येक आइटमSelectMany को देखने के लिए उपयोग कर रहे हैं ।

मुझे लगता है कि आप बस चाहते हैं:

List<ResultLine> result = Lines
    .GroupBy(l => l.ProductCode)
    .Select(cl => new ResultLine
            {
                ProductName = cl.First().Name,
                Quantity = cl.Count().ToString(),
                Price = cl.Sum(c => c.Price).ToString(),
            }).ToList();

First()उत्पाद का नाम प्राप्त करने के लिए यहाँ का उपयोग मानता है कि समान उत्पाद कोड वाले प्रत्येक उत्पाद का उत्पाद नाम समान है। जैसा कि टिप्पणियों में कहा गया है, आप उत्पाद नाम के साथ-साथ उत्पाद कोड भी समूह कर सकते हैं, जो समान परिणाम देगा यदि नाम किसी भी दिए गए कोड के लिए हमेशा समान होता है, लेकिन जाहिरा तौर पर EF में बेहतर SQL उत्पन्न करता है।

मैं यह भी सुझाव दूंगा कि आपको Quantityऔर Priceगुणों को क्रमशः intऔर decimalप्रकारों में बदलना चाहिए - डेटा के लिए एक स्ट्रिंग प्रॉपर्टी का उपयोग क्यों करें जो स्पष्ट रूप से पाठीय नहीं है?


ठीक है मेरा कंसोल ऐप काम कर रहा है। मुझे पहले का उपयोग करने के लिए इंगित करने के लिए धन्यवाद () और SelectMany को छोड़ दें। ResultLine वास्तव में एक ViewModel है। मुद्रा संकेत के साथ मूल्य स्वरूपित होगा। इसलिए मुझे इसे एक स्ट्रिंग होने की आवश्यकता है। लेकिन मैं मात्रा को int में बदल सकता हूं .. अब मैं देखूंगा कि क्या यह मेरी वेबसाइट के लिए भी मदद कर सकता है। मैं आपको बता दूँगा।
ThdK

6
@ थीडीके: नहीं, आपको Priceएक दशमलव के रूप में भी रखना चाहिए , और फिर आप इसे कैसे प्रारूपित करें, इसे बदलें। डेटा प्रतिनिधित्व को साफ रखें, और केवल अंतिम संभव समय पर एक प्रस्तुति दृश्य में बदलें।
जॉन स्कीट

4
क्यों नहीं ProductCode और नाम से समूह के लिए? कुछ इस तरह:: .GroupBy (l => new {l.ProductCode, l.Name}) और ProductName = c.Key.Name का उपयोग करें,
Kirill Bestemyanov

@KirillBestemyanov: हाँ, यह एक और विकल्प है, निश्चित रूप से।
जॉन स्कीट

ठीक है, ऐसा लगता है कि मेरा संग्रह वास्तव में वास्तविक संग्रह के आसपास एक आवरण था .. मुझे गोली मारो .. अच्छी बात है, मैंने आज कुछ Linq अभ्यास किया :)
ThdK

27

निम्नलिखित क्वेरी काम करती है। यह प्रत्येक समूह का उपयोग करने के बजाय चयन करने के लिए करता है SelectManySelectManyप्रत्येक संग्रह से प्रत्येक तत्व पर काम करता है। उदाहरण के लिए, आपकी क्वेरी में आपके पास 2 संग्रहों का परिणाम है। SelectManyप्रत्येक संग्रह के बजाय सभी परिणाम, कुल 3 प्राप्त होते हैं। निम्न कोड IGroupingआपके समग्र संचालन को सही ढंग से काम करने के लिए चयनित भाग में प्रत्येक पर काम करता है।

var results = from line in Lines
              group line by line.ProductCode into g
              select new ResultLine {
                ProductName = g.First().Name,
                Price = g.Sum(pc => pc.Price).ToString(),
                Quantity = g.Count().ToString(),
              };

2

कभी-कभी आपको कुछ फ़ील्ड चुनने की आवश्यकता होती है FirstOrDefault()या singleOrDefault()आप नीचे दिए गए क्वेरी का उपयोग कर सकते हैं:

List<ResultLine> result = Lines
    .GroupBy(l => l.ProductCode)
    .Select(cl => new Models.ResultLine
            {
                ProductName = cl.select(x=>x.Name).FirstOrDefault(),
                Quantity = cl.Count().ToString(),
                Price = cl.Sum(c => c.Price).ToString(),
            }).ToList();

1
क्या आप बता सकते हैं कि कभी-कभी मुझे FirstOrDefault() or सिंगलऑर्फ़ॉल्ट () का उपयोग करने की आवश्यकता क्यों है ?
शांतेश्वर इंडे

@ शांतेश्वरइंड प्रथम () और फ़र्स्टऑडडेफ़ॉल्ट () को एक श्रंखला में पहली वस्तु मिलती है, जबकि सिंगल () और सिंगलऑर्फ़ॉल्ट () परिणाम से केवल 1 की अपेक्षा करती है। यदि सिंगल () और सिंगलऑर्डेफ़ॉल्ट () देखता है कि परिणाम सेट में 1 से अधिक ऑब्जेक्ट हैं या प्रदान किए गए तर्क के परिणामस्वरूप, यह एक अपवाद फेंक देगा। उपयोग करने पर, आप पूर्व का उपयोग तब करते हैं जब आप केवल चाहते हैं, हो सकता है कि एक श्रृंखला का एक नमूना और अन्य वस्तुएं आपके लिए महत्वपूर्ण नहीं हैं, जबकि आप बाद का उपयोग करते हैं यदि आप केवल एक वस्तु की उम्मीद कर रहे हैं और कुछ करते हैं यदि एक से अधिक परिणाम हैं , जैसे त्रुटि लॉग।
क्रिस्टियान नेरोना
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.