2010-10-11 20 views
21

nHibernate के लिए धन्यवाद, मैं जिन डेटा संरचनाओं के साथ काम करता हूं वे सूचियों के भीतर सूचियों के भीतर सूचियां हैं। तो उदाहरण के लिए मेरे पास "श्रेणी" नामक एक डेटा ऑब्जेक्ट है जिसमें एक है। हल्की संपत्ति जो श्रेणियों की सूची में हल होती है ... जिनमें से प्रत्येक में बच्चे हो सकते हैं ... और इसी तरह से।एक बयान के साथ एक पेड़ (सूचियों की सूची) flatten?

मुझे इस संरचना में शीर्ष-स्तरीय श्रेणी में शुरू करने और पूरी संरचना में सभी बच्चों के समान सूची या सरणी या कुछ प्राप्त करने का एक तरीका ढूंढना है - इसलिए सभी बच्चों के सभी बच्चे इत्यादि, एक सूची में चपटा।

मुझे यकीन है कि इसे रिकर्सन के साथ किया जा सकता है, लेकिन मुझे रिकर्सिव कोड को दर्द करने के लिए दर्द मिलता है, और मुझे विश्वास है कि लिंक 4 या किसी भी सुझाव का उपयोग करके नेट 4 में एक और सीधा तरीका होना चाहिए - कोई सुझाव?

+0

एक पेड़ को सपाट करना स्वाभाविक रूप से पुनरावर्ती लगता है। मुझे विश्वास नहीं है कि LINQ के साथ भी एक पेड़ को फटकारने का एक ही कथन तरीका है। क्या आप एक रिकर्सिव उत्तर स्वीकार करेंगे? – climbage

+1

निश्चित रूप से। यह उन चीजों में से एक है जो ऐसा लगता है कि एक "आसान" जवाब होना चाहिए।लिंक "selectMany" एक पेड़ के दो स्तरों को चपटा देगा, लेकिन मुसीबत यह है कि मेरे पास यह जानने का कोई तरीका नहीं है कि जब मैं शुरू करता हूं तो मेरे ऑब्जेक्ट में मुझे कितने स्तर मिलते हैं। तो मुझे लगता है कि रिकर्सन जाने का एकमात्र तरीका है। –

उत्तर

12

अपने श्रेणी वर्ग मान लिया जाये कि तरह दिखता है:

public class Category 
{ 
    public string Name { get; set; } 
    public List<Category> Children { get; set; } 
} 

मैं वहाँ एक "आसान" गैर पुनरावर्ती यह करने के लिए जिस तरह से नहीं लगता कि; यदि आप इसे संभालने के लिए बस एक विधि कॉल की तलाश में हैं, तो "आसान" तरीका रिकर्सिव संस्करण को एक विधि विधि में लिखना है। ऐसा करने के लिए शायद एक पुनरावृत्ति तरीका है, लेकिन मुझे लगता है कि यह वास्तव में बहुत जटिल है। यह कैलकुस का उपयोग किए बिना एक वक्र को टेंगेंट खोजने का "आसान" तरीका पूछने जैसा है।

वैसे भी, यह शायद यह करना होगा:

// Depth-first traversal, recursive 
public static IEnumerable<T> Flatten<T>(
     this IEnumerable<T> source, 
     Func<T, IEnumerable<T>> childrenSelector) 
{ 
    foreach (var item in source) 
    { 
     yield return item; 
     foreach (var child in childrenSelector(item).Flatten(childrenSelector)) 
     { 
      yield return child; 
     } 
    } 
} 

आप इस तरह इसका इस्तेमाल कर सकते हैं::

foreach(var category in categories.Flatten(c => c.Children)) 
{ 
    ... 
} 

public static List<Category> Flatten(Category root) { 

    var flattened = new List<Category> {root}; 

    var children = root.Children; 

    if(children != null) 
    { 
     foreach (var child in children) 
     { 
      flattened.AddRange(Flatten(child)); 
     } 
    } 

    return flattened; 
} 
+0

* एक आसान गैर-पुनरावर्ती समाधान है, लेकिन यह एक चौड़ाई-पहले ट्रैवर्सल (मेरा जवाब देखें) करता है। शायद यह एक बड़ा मुद्दा नहीं है, लेकिन यह निर्भर करता है कि ओपी वास्तव में क्या चाहता है ... –

36

यहाँ एक विस्तार विधि है कि काम करता है उपरोक्त समाधान गहराई से पहले ट्रैवर्सल करता है, यदि आप एक चौड़ाई-पहले ट्रैवर्सल चाहते हैं तो आप वें कुछ कर सकते हैं है:

// Breadth-first traversal, non-recursive 
public static IEnumerable<T> Flatten2<T>(
     this IEnumerable<T> source, 
     Func<T, IEnumerable<T>> childrenSelector) 
{ 
    var queue = new Queue<T>(source); 
    while (queue.Count > 0) 
    { 
     var item = queue.Dequeue(); 
     yield return item; 
     foreach (var child in childrenSelector(item)) 
     { 
      queue.Enqueue(child); 
     } 
    } 
} 

यह भी गैर पुनरावर्ती होने का लाभ है ...


अद्यतन: असल में, मैं सिर्फ एक तरह से गहराई-प्रथम ट्रेवर्सल गैर पुनरावर्ती बनाने के लिए के बारे में सोचा। यहाँ .. यह है:

// Depth-first traversal, non-recursive 
public static IEnumerable<T> Flatten3<T>(
     this IEnumerable<T> source, 
     Func<T, IEnumerable<T>> childrenSelector) 
{ 
    LinkedList<T> list = new LinkedList<T>(source); 
    while (list.Count > 0) 
    { 
     var item = list.First.Value; 
     yield return item; 
     list.RemoveFirst(); 
     var node = list.First; 
     foreach (var child in childrenSelector(item)) 
     { 
      if (node != null) 
       list.AddBefore(node, child); 
      else 
       list.AddLast(child); 
     } 
    } 
} 

मैं उपयोग कर रहा हूँ एक LinkedList<T> क्योंकि सम्मिलन हे हैं (1) के संचालन, जबकि एक List<T> के सम्मिलन हे (एन) कर रहे हैं।

+1

कतार-आधारित समाधान पर चतुरता के लिए +1। मुझे नहीं पता कि मैं इसे "आसान" कहूंगा, लेकिन फिर मुझे लगता है कि यह राय का विषय है। और चौड़ाई बनाम गहराई - पहला सवाल एक महत्वपूर्ण है; मुझे पहले गहराई से लगता है, और यह अंदर आने की एक बुरी आदत है। वैसे भी, आपका जवाब बेहतर है। –

+0

+1 बहुत उपयोगी, धन्यवाद। –

+0

मेरे लिए बहुत अच्छा काम किया, धन्यवाद =) – afreeland

1

तो चौड़ाई-पहले ट्रेवर्सल ठीक है और आप केवल कुछ कम गैर पुनरावर्ती इनलाइन कोड (3 लाइनों वास्तव में) का उपयोग करना चाहते हैं, अपने मूल तत्व (रों) और उसके बाद के साथ प्रारंभ एक सूची बनाने के लिए सिर्फ एक सरल से यह विस्तार -loop:

// your data object class looks like: 
public class DataObject 
{ 
    public List<DataObject> Children { get; set; } 
    ... 
} 

... 

// given are some root elements 
IEnumerable<DataObject> rootElements = ... 

// initialize the flattened list with the root elements 
var result = new List<DataObject>(rootElements); 
// extend the flattened list by one simple for-loop, 
// please note that result.Count may increase after every iteration! 
for (int index = 0; index < result.Count; index++) 
    result.AddRange(result[index].Children); 
+0

क्या होगा यदि आपका पेड़ 1 से अधिक गहरा है? यह काम नहीं करेगा। – rolls

+0

@rolls सूची looped होने के दौरान खुद को फैलाता है। इस तरह यह सभी स्तरों पर सभी तत्वों को पार करेगा। –

+0

यह चालाक है कि मुझे याद आया। मेरा मानना ​​है कि यदि आप एक लिंक-सूची का उपयोग करते हैं तो AddRange भी अधिक प्रदर्शनशील होगा। – rolls

0

वर्ग @EZHart का उल्लेख है को देखते हुए, आप भी यह इस जो मुझे लगता है कि इस मामले में आसान है के लिए एक सहायक विधि के साथ विस्तार कर सकता है।

public class Category 
{ 
    public string Name { get; set; } 

    public List<Category> Children { get; set; } 

    public IEnumerable<Category> AllChildren() 
    { 
     yield return this; 
     foreach (var child in Children) 
     foreach (var granChild in child.AllChildren()) 
     { 
      yield return granChild; 
     } 
    } 
} 
संबंधित मुद्दे