LINQ

2009-04-06 6 views
132

के साथ दो वस्तु सूची से एक सूची बनाएं मैं इस स्थितिLINQ

class Person 
{ 
    string Name; 
    int Value; 
    int Change; 
} 

List<Person> list1; 
List<Person> list2; 

मैं इसे एक ही व्यक्ति गठबंधन रिकॉर्ड है कि नाम के लिए होता है के मामले में एक नया List<Person> में 2 सूचियां संयोजित करने की जरूरत है निम्नलिखित है, सूची 2 में व्यक्ति का मूल्य, परिवर्तन सूची 2 का मूल्य होगा - सूची 1 का मूल्य। कोई डुप्लिकेट

+1

क्या linq वास्तव में जरूरी है - लिनक-आश अभिव्यक्तियों के साथ एक अच्छा foreach भी कर सकता है। – Rashack

+1

इस टिप्पणी को प्रश्न के शीर्षक के संस्करण के रूप में जोड़ना और वास्तविक प्रश्न मेल नहीं खाता: इसका वास्तविक जवाब [माइक से यह जवाब] है (http://stackoverflow.com/a/6772832/1542187)। अधिकांश अन्य उत्तरों, उपयोगी होने पर, मूल पोस्टर द्वारा प्रस्तुत समस्या को वास्तव में हल नहीं करते हैं। –

उत्तर

11

ऐसा करने के लिए कुछ टुकड़े हैं, मानते हैं कि प्रत्येक सूची में डुप्लीकेट नहीं होते हैं, नाम एक अद्वितीय पहचानकर्ता है, और न ही सूची का आदेश दिया जाता है।

पहले एक एकल सूची प्राप्त करने की संलग्न विस्तार विधि बनाने के लिए:

static class Ext { 
    public static IEnumerable<T> Append(this IEnumerable<T> source, 
             IEnumerable<T> second) { 
    foreach (T t in source) { yield return t; } 
    foreach (T t in second) { yield return t; } 
    } 
} 

इस प्रकार एक सूची प्राप्त कर सकते हैं:

var oneList = list1.Append(list2); 

फिर नाम पर समूह

var grouped = oneList.Group(p => p.Name); 

तो कर सकते हैं

एक समय में एक समूह को संसाधित करने के लिए प्रत्येक समूह को एक सहायक के साथ संसाधित करें
public Person MergePersonGroup(IGrouping<string, Person> pGroup) { 
    var l = pGroup.ToList(); // Avoid multiple enumeration. 
    var first = l.First(); 
    var result = new Person { 
    Name = first.Name, 
    Value = first.Value 
    }; 
    if (l.Count() == 1) { 
    return result; 
    } else if (l.Count() == 2) { 
    result.Change = first.Value - l.Last().Value; 
    return result; 
    } else { 
    throw new ApplicationException("Too many " + result.Name); 
    } 
} 

कौन सा grouped के प्रत्येक तत्व के लिए लागू किया जा सकता है:

var finalResult = grouped.Select(g => MergePersonGroup(g)); 

(चेतावनी:। अपरीक्षित)

+2

आपका 'एपेंड' आउट ऑफ़ द बॉक्स 'कंसैट' का लगभग सटीक डुप्लिकेट है। – Rawling

+0

@ राउलिंग: यह किसी कारण से मैंने 'एन्यूमेरेबल.कोनकैट' खो दिया और इस प्रकार इसे फिर से कार्यान्वित किया। – Richard

2

आप एक पूरे बाहरी में शामिल होने की तरह कुछ की जरूरत है। System.Linq.Numerable में कोई तरीका नहीं है जो पूर्ण बाहरी जुड़ने को लागू करता है, इसलिए हमें इसे स्वयं करना है।

var dict1 = list1.ToDictionary(l1 => l1.Name); 
var dict2 = list2.ToDictionary(l2 => l2.Name); 
    //get the full list of names. 
var names = dict1.Keys.Union(dict2.Keys).ToList(); 
    //produce results 
var result = names 
.Select(name => 
{ 
    Person p1 = dict1.ContainsKey(name) ? dict1[name] : null; 
    Person p2 = dict2.ContainsKey(name) ? dict2[name] : null; 
     //left only 
    if (p2 == null) 
    { 
    p1.Change = 0; 
    return p1; 
    } 
     //right only 
    if (p1 == null) 
    { 
    p2.Change = 0; 
    return p2; 
    } 
     //both 
    p2.Change = p2.Value - p1.Value; 
    return p2; 
}).ToList(); 
224

यह आसानी से लिंक एक्सटेंशन विधि संघ का उपयोग करके किया जा सकता है। उदाहरण के लिए:

var mergedList = list1.Union(list2).ToList(); 

यह एक सूची लौटाएगा जिसमें दो सूचियां विलय हो गई हैं और युगल हटा दिए गए हैं। यदि आप मेरे उदाहरण में यूनियन एक्सटेंशन विधि में एक तुलनाकर्ता निर्दिष्ट नहीं करते हैं, तो यह आपके व्यक्तिगत वर्ग में डिफ़ॉल्ट बराबर और GetHashCode विधियों का उपयोग करेगा। यदि आप उदाहरण के लिए अपनी नाम संपत्ति की तुलना करके व्यक्तियों की तुलना करना चाहते हैं, तो आपको तुलना करने के लिए इन विधियों को ओवरराइड करना होगा। इसे पूरा करने के लिए निम्न कोड नमूना देखें। आपको यह कोड अपनी व्यक्तिगत कक्षा में जोड़ना होगा।

/// <summary> 
/// Checks if the provided object is equal to the current Person 
/// </summary> 
/// <param name="obj">Object to compare to the current Person</param> 
/// <returns>True if equal, false if not</returns> 
public override bool Equals(object obj) 
{   
    // Try to cast the object to compare to to be a Person 
    var person = obj as Person; 

    return Equals(person); 
} 

/// <summary> 
/// Returns an identifier for this instance 
/// </summary> 
public override int GetHashCode() 
{ 
    return Name.GetHashCode(); 
} 

/// <summary> 
/// Checks if the provided Person is equal to the current Person 
/// </summary> 
/// <param name="personToCompareTo">Person to compare to the current person</param> 
/// <returns>True if equal, false if not</returns> 
public bool Equals(Person personToCompareTo) 
{ 
    // Check if person is being compared to a non person. In that case always return false. 
    if (personToCompareTo == null) return false; 

    // If the person to compare to does not have a Name assigned yet, we can't define if it's the same. Return false. 
    if (string.IsNullOrEmpty(personToCompareTo.Name) return false; 

    // Check if both person objects contain the same Name. In that case they're assumed equal. 
    return Name.Equals(personToCompareTo.Name); 
} 

आप डिफ़ॉल्ट के बराबर है अपने व्यक्ति वर्ग हमेशा नाम का उपयोग करने के दो वस्तुओं की तुलना करने के लिए की विधि स्थापित करने के लिए नहीं करना चाहते हैं, तो आप भी एक comparer वर्ग जो IEqualityComparer इंटरफ़ेस का उपयोग करता लिख ​​सकते हैं। फिर आप इस तुलनाकर्ता को लिंक एक्सटेंशन यूनियन विधि में दूसरे पैरामीटर के रूप में प्रदान कर सकते हैं। इस तरह की तुलनात्मक विधि को लिखने के तरीके के बारे में अधिक जानकारी http://msdn.microsoft.com/en-us/library/system.collections.iequalitycomparer.aspx

+10

मुझे नहीं लगता कि यह मूल्यों के विलय के बारे में प्रश्न का उत्तर कैसे देता है। – wdanda

+1

यह प्रतिक्रिया नहीं देता है, संघ में केवल दो सेटों में मौजूद आइटम शामिल होंगे, दो सूची में से किसी एक में मौजूद कोई भी तत्व – J4N

+5

@ जे 4 एन क्या आप संभवत: 'इंटरसेक्ट' के साथ 'संघ' को भ्रमित कर रहे हैं? – Kos

53

मैंने देखा कि यह प्रश्न 2 वर्षों के बाद उत्तर के रूप में चिह्नित नहीं किया गया था - मुझे लगता है कि सबसे नज़दीकी उत्तर रिचर्ड्स है, लेकिन इसे काफी सरल बनाया जा सकता है इस:

list1.Concat(list2) 
    .ToLookup(p => p.Name) 
    .Select(g => g.Aggregate((p1, p2) => new Person 
    { 
     Name = p1.Name, 
     Value = p1.Value, 
     Change = p2.Value - p1.Value 
    })); 

इस हालांकि मामले या तो आप सेट में डुप्लिकेट नाम जहां में नहीं त्रुटि होगा।

कुछ अन्य उत्तरों ने संघटन का उपयोग करने का सुझाव दिया है - यह निश्चित रूप से जाने का तरीका नहीं है क्योंकि यह संयोजन के बिना आपको केवल एक अलग सूची प्राप्त करेगा।

+6

यह पोस्ट वास्तव में प्रश्न का उत्तर देता है, और यह अच्छी तरह से करता है। – philu

+2

यह स्वीकार्य उत्तर होना चाहिए। उन प्रश्नों के लिए इतने सारे उपरोक्त प्रश्नों के साथ कभी प्रश्न नहीं देखा जो पूछे गए प्रश्न का उत्तर नहीं देते! –

1
public void Linq95() 
{ 
    List<Customer> customers = GetCustomerList(); 
    List<Product> products = GetProductList(); 

    var customerNames = 
     from c in customers 
     select c.CompanyName; 
    var productNames = 
     from p in products 
     select p.ProductName; 

    var allNames = customerNames.Concat(productNames); 

    Console.WriteLine("Customer and product names:"); 
    foreach (var n in allNames) 
    { 
     Console.WriteLine(n); 
    } 
} 
60

आप केवल Concat का उपयोग क्यों नहीं करते?

List<Person> list1 = ... 
List<Person> list2 = ... 
List<Person> total = list1.Concat(list2); 
+12

आप कैसे जानते हैं कि यह अधिक कुशल है? –

+0

@ जेरी निक्सन उन्होंने परीक्षण नहीं किया, लेकिन स्पष्टीकरण तार्किक लगता है। http://stackoverflow.com/questions/1337699/which-is-faster-union-or-concat – Nullius

+7

http: // stackoverflow।कॉम/प्रश्न/100196/नेट-लिस्ट-कॉन्सैट-बनाम-एड्रेंज -> ग्रेग की टिप्पणी: 'वास्तव में, स्थगित निष्पादन के कारण, कोंकैट का उपयोग करना तेजी से तेज़ होगा क्योंकि यह ऑब्जेक्ट आवंटन से बचाता है - Concat कुछ भी कॉपी नहीं करता है, यह सिर्फ सूचियों के बीच लिंक बनाता है ताकि गणना करते समय और आप एक के अंत तक पहुंच सकें, यह आपको पारदर्शी रूप से अगले की शुरुआत में ले जाता है! 'यह मेरा मुद्दा है। – J4N

2

करता है आपकी समस्या के लिए निम्न कोड काम:

Concat LINQ का एक हिस्सा है और एक AddRange()

अपने मामले में

कर से अधिक कुशल है? मैंने सूचियों के संयोजन के लिए अंदर लिनक के साथ एक फोरच का उपयोग किया है और माना है कि अगर उनके नाम मेल खाते हैं तो लोग बराबर हैं, और ऐसा लगता है कि रन होने पर अपेक्षित मूल्यों को प्रिंट करना प्रतीत होता है। Resharper foreach को linq में रूपांतरित करने के लिए कोई सुझाव नहीं देता है, इसलिए यह संभवतः उतना ही अच्छा होगा जितना इसे इस तरह से कर देगा।

public class Person 
{ 
    public string Name { get; set; } 
    public int Value { get; set; } 
    public int Change { get; set; } 

    public Person(string name, int value) 
    { 
     Name = name; 
     Value = value; 
     Change = 0; 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Person> list1 = new List<Person> 
           { 
           new Person("a", 1), 
           new Person("b", 2), 
           new Person("c", 3), 
           new Person("d", 4) 
           }; 
     List<Person> list2 = new List<Person> 
           { 
           new Person("a", 4), 
           new Person("b", 5), 
           new Person("e", 6), 
           new Person("f", 7) 
           }; 

     List<Person> list3 = list2.ToList(); 

     foreach (var person in list1) 
     { 
     var existingPerson = list3.FirstOrDefault(x => x.Name == person.Name); 
     if (existingPerson != null) 
     { 
      existingPerson.Change = existingPerson.Value - person.Value; 
     } 
     else 
     { 
      list3.Add(person); 
     } 
     } 

     foreach (var person in list3) 
     { 
     Console.WriteLine("{0} {1} {2} ", person.Name,person.Value,person.Change); 
     } 
     Console.Read(); 
    } 
} 
6

यह Linq

var mergedList = list1.Union(list2).ToList(); 

यह Normaly है (AddRange)

var mergedList=new List<Person>(); 
mergeList.AddRange(list1); 
mergeList.AddRange(list2); 

यह Normaly है (Foreach)

var mergedList=new List<Person>(); 

foreach(var item in list1) 
{ 
    mergedList.Add(item); 
} 
foreach(var item in list2) 
{ 
    mergedList.Add(item); 
} 

यह Normaly है (Foreach-Dublice है)

var mergedList=new List<Person>(); 

foreach(var item in list1) 
{ 
    mergedList.Add(item); 
} 
foreach(var item in list2) 
{ 
    if(!mergedList.Contains(item)) 
    { 
    mergedList.Add(item); 
    } 
} 
संबंधित मुद्दे