2012-01-12 8 views
7

मेरे पास question using these same examples है - यह प्रश्न एक अलग मुद्दे पर केंद्रित है। को देखते हुए निम्नलिखित वर्गों:नेस्टेड क्लास सूची <T> से एक फ़्लैटेड सूची कैसे प्राप्त करें?

[XmlRoot] 
    public class Family { 

     [XmlElement] 
     public List<Person> Person; 
    } 

    public class Person { 

     [XmlAttribute("member")] 
     public MemberType Member { get; set; } 

     [XmlAttribute("id")] 
     public int Id { get; set; } 

     [XmlElement] 
     public string Surname { get; set; } 

     [XmlElement] 
     public string Forename { get; set; } 

     [XmlElement("Person")] 
     public List<Person> People; 
    } 

    public enum MemberType { 
     Father, 
     Mother, 
     Son, 
     Daughter 
    } 

तो Family एक विधि इस तरह के रूप में परिभाषित किया गया है:

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    // how do I get SelectMany to flatten the list? 
    foreach (var p in family.Person.SelectMany(p => p)) { 
     if(predicate(p)) { 
      yield return p; 
     } 
    } 
} 

मैं Person का एक चपटा सूची पर विधेय निष्पादित करने में सक्षम होने की जरूरत है। ऊपर दिए गए उदाहरण में SelectMany सूची को फ़्लैटन नहीं कर रहा है जैसा कि मैंने आशा की थी। उपर्युक्त वास्तव में संकलित नहीं होगा क्योंकि अनुमानित प्रकार निर्धारित नहीं किया जा सकता है।

मैं परिवार कैसे प्राप्त कर सकता हूं। व्यक्ति संग्रह व्यक्ति की एक चतुर सूची बनने के लिए?

+0

तुम कोशिश 'पी => p.People' किया? – jvstech

+0

यदि आपके पास अपनी डेटा संरचना में लूप हैं तो आप यह समाधान कर सकते हैं: http://stackoverflow.com/questions/141467/recursive-list-flattening/24747394#answer-24747394 – Aidin

उत्तर

5
public IEnumerable<Person> Find(IEnumerable<Person> input, Func<Person, bool> predicate) { 
    return input.Select(p => 
     { 
      var thisLevel = new List<Person>(); 
      if(predicate(p)) 
       thisLevel.Add(p); 

      return thisLevel.Union(Find(p.People ?? new List<Person>(), predicate)); 
     } 
    ).SelectMany(p => p); 
} 
+0

+1, प्रश्न ने इस बिंदु को पर्याप्त स्पष्ट नहीं किया :) – MattDavey

+2

मैं संघ पर Concat की अनुशंसा करता हूं क्योंकि क्रॉस-सेट डुप्लिकेट के लिए स्क्रीन की आवश्यकता नहीं है। –

2

family.Personपहले से ही एक फ़्लैटेड सूची है; SelectMany पर कॉल करने की आवश्यकता नहीं है।

foreach (var p in family.Person) { 
    if(predicate(p)) { 
     yield return p; 
    } 
} 

इसके अलावा, आप और अधिक बस कर सकते हैं:

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    return family.Person.Where(predicate); 
} 
+0

ध्यान दें कि व्यक्ति के पास नेस्टेड * सूची * है। यह ** ** एक चतुर सूची नहीं है, यह एक पुनरावर्ती डेटा संरचना है। – MattDavey

+1

@ मैट - मैंने पूरी तरह से यह नहीं देखा - खासकर ओपी के 'परिवार' के मूल कोड पर विचार करना। पीटरसन। चयन करें (पी => पी) 'मैं ठीक करने के लिए संपादित करूँगा, लेकिन ऐसा लगता है जैसे पेट्र ने इसे पार्क से बाहर कर दिया –

3

आप दोनों SelectMany और yield return जरूरत नहीं है - आप एक या दूसरे की जरूरत है:

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    foreach (var p in family.Person) { 
     if(predicate(p)) { 
      yield return p; 
     } 
    } 
} 

या

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    return family.Person.Where(p => predicate(p)); // Can be written simply as Where(predicate) 
} 
+1

* 'p => predicate (p)' * को केवल * 'predicate' * में कम किया जा सकता है - एटीएम आप एक * Func * को दूसरे के साथ लपेट रहे हैं। – MattDavey

+1

@MattDavey मैंने अभिव्यक्ति से "जादू" को हटाने के लिए एक रैपर जोड़ा। मैंने यह कहने के लिए एक टिप्पणी पंक्ति जोड़ा कि इसे सरल बनाया जा सकता है। यह सिर्फ इतना है कि 'कहां' के बिना 'कहां' खंड को देखकर अपेक्षाकृत कम LINQ अनुभव वाले लोगों को भ्रमित किया जा सकता है। – dasblinkenlight

+0

हाँ मुझे लगता है कि यह करने के लिए एक उचित बात है :) – MattDavey

6

मेरे ज्ञान के लिए, इसे पूरा करने का सबसे आसान तरीका एक सहायक का उपयोग कर रहा है।

private List<Person> FlattenTree(Person person) 
    { 
     var accumulator = new List<Person>(); 
     FlattenPersonHelper(person, accumulator); 

     return accumulator; 
    } 


    private void FlattenPersonHelper(Person person, List<Person> accumulator) 
    { 
     accumulator.Add(person); 

     foreach (var child in person.People) 
     { 
      FlattenPersonHelper(child, accumulator); 
     } 
     return; 
    } 

फिर आप इस सूची के खिलाफ अपने विधेय चला सकते हैं:

public IEnumerable<Person> FindLevel2 (Func<Person, bool> predicate) 
{ 
    return family.Person.SelectMany(p => p.People).Where(predicate); 
} 

आप एक मनमाना गहराई की पैदल दूरी पर वास्तव में चाहते हो सकता है:

public IEnumerable<Person> Find (Func<Person, bool> predicate) { 
    var familyRoot = new Person() { People = family.Person }; 
    return FlattenTree(familyRoot).Where(predicate); 
} 
+2

+1 मुझे यह पसंद है - stackoverflow में आपका स्वागत है! –

+1

धन्यवाद! और धन्यवाद! पीएस मैं इस पैटर्न का उपयोग अक्सर इतना करता हूं कि मैंने एक विस्तार विधि के रूप में एक सामान्य संस्करण लिखा है। सुपर उपयोगी रिकर्सिव डेटा स्ट्रक्चर को ढूंढने के लिए –

4

SelectMany केवल पदानुक्रम में से एक स्तर सपाट पदानुक्रम का। यह सबसे अच्छा रिकर्सन (अनचाहे) द्वारा किया जाता है।

public IEnumerable<Person> Find(Func<Person, bool> predicate) 
{ 
    foreach(Person p in family.Person) 
    { 
    IEnumerable<Person> result = FindFromPerson(p); 
    foreach(Person x in result) 
    { 
     yield return x; 
    } 
    } 
} 

public IEnumerable<Person> FindFromPerson(Person p, Func<Person, bool> predicate) 
{ 
    if predicate(p) 
    { 
    yield return p; 
    } 
    foreach(Person child in p.People) 
    { 
    IEnumerable<Person> childResults = FindFromPerson(child); 
    foreach(Person x in childResults) 
    { 
     yield return x; 
    } 
    } 
} 
संबंधित मुद्दे