2010-04-08 8 views
6

मेरे पास तत्वों की एक सरणी है जहां तत्व के पास एक ध्वजांकित बूलियन मान है।लिंक का उपयोग एरेज़ में डेटा विभाजित करने के लिए

1 flagged 
2 not flagged 
3 not flagged 
4 flagged 
5 not flagged 
6 not flagged 
7 not flagged 
8 flagged 
9 not flagged 

मैं चिह्नित किये सूचक के आधार पर

उत्पादन विन्यास में, इसे तोड़ने के लिए चाहते हैं>

array 1 {1,2,3} 
array 2 {4,5,6,7} 
array 3 {8,9} 

उत्तर

0

मुझे नहीं लगता कि LINQ बहुत अच्छी तरह से इस के लिए अनुकूल है है। यह कुल() के साथ किया जा सकता है, लेकिन मुझे लगता है कि आप परिणाम को बनाने के लिए केवल एक foreach() के साथ लूपिंग बेहतर होगा।

7

Linq इस के लिए एक ऑपरेटर नहीं है, लेकिन मैं एक विस्तार विधि है कि आप उपयोग करने में सक्षम हो सकता है (MoreLinq, को भेजने की प्रक्रिया में जो आप भी जांच करनी चाहिए) लिखा है:

ऑपरेटर नीचे का उपयोग करना, आप लिखते हैं:

public static IEnumerable<IEnumerable<T>> Segment<T>(IEnumerable<T> sequence, Func<T, T, int, bool> newSegmentIdentifier) 
    { 
     var index = -1; 
     using (var iter = sequence.GetEnumerator()) 
     { 
      var segment = new List<T>(); 
      var prevItem = default(T); 

      // ensure that the first item is always part 
      // of the first segment. This is an intentional 
      // behavior. Segmentation always begins with 
      // the second element in the sequence. 
      if (iter.MoveNext()) 
      { 
       ++index; 
       segment.Add(iter.Current); 
       prevItem = iter.Current; 
      } 

      while (iter.MoveNext()) 
      { 
       ++index; 
       // check if the item represents the start of a new segment 
       var isNewSegment = newSegmentIdentifier(iter.Current, prevItem, index); 
       prevItem = iter.Current; 

       if (!isNewSegment) 
       { 
        // if not a new segment, append and continue 
        segment.Add(iter.Current); 
        continue; 
       } 
       yield return segment; // yield the completed segment 

       // start a new segment... 
       segment = new List<T> { iter.Current }; 
      } 
      // handle the case of the sequence ending before new segment is detected 
      if (segment.Count > 0) 
       yield return segment; 
     } 
    } 
+0

लैम्ब्डा समारोह में हालत क्यों है ' item.Flagged! = prevItem.Flagged '? ध्वज गलत से बदलते हैं तो आप एक नया खंड बनायेंगे। मुझे लगता है कि स्थिति सिर्फ 'आइटम' फ्लेग किया जाना चाहिए, और पिछले आइटम से बिल्कुल निर्भर नहीं होना चाहिए। –

+0

@phild: आप सही हैं, 'item.Flagged' पर्याप्त है। मैं पहले जल्दी में था और ओपी की उम्मीदों का गलत व्याख्या करता था। मैंने अपनी प्रतिक्रिया अपडेट की है। – LBushkin

+0

+1, बहुत उपयोगी विस्तार विधि :) –

1

मुझे नहीं लगता कि LINQ थी के लिए सही उपकरण है है:

var result = 
    items.Segment((item,prevItem,idx) => item.Flagged) 
     .Select(seq => seq.ToArray()) // converts each sequence to an array 
     .ToList(); 

यहाँ विस्तार विधि का कोड है एस कार्य क्या इस बारे में:

public static List<List<T>> PartitionData<T>(T[] arr, Func<T, bool> flagSelector){ 
    List<List<T>> output = new List<List<T>>(); 
    List<T> partition = null; 
    bool first = true; 

    foreach(T obj in arr){ 
     if(flagSelector(obj) || first){ 
      partition = new List<T>(); 
      output.Add(partition); 
      first = false; 
     } 
     partition.Add(obj); 
    } 

    return output; 
} 

एक छोटा सा उदाहरण, Fábio Batistas पद से डाटा के साथ:

var arrayOfElements = new[] { 
    new { Id = 1, Flagged = true }, 
    new { Id = 2, Flagged = false }, 
    new { Id = 3, Flagged = false }, 
    new { Id = 4, Flagged = true }, 
    new { Id = 5, Flagged = false }, 
    new { Id = 6, Flagged = false }, 
    new { Id = 7, Flagged = false }, 
    new { Id = 8, Flagged = true }, 
    new { Id = 9, Flagged = false } 
}; 

var partitioned = PartitionData(arrayOfElements, x => x.Flagged); 
+0

मुझे लगता है कि इसे आउटपुट की आवश्यकता है। जोड़ें (वर्तमान सूची); foreach स्टेटमेंट के बाद, या आपका अंतिम सरणी कभी नहीं जोड़ा जाता है। – Kenoyer130

+0

मैंने इसे अपडेट किया, और इसके बारे में एक सामान्य कार्य किया। –

2

को ध्यान में रखते:

var arrayOfElements = new[] { 
    new { Id = 1, Flagged = true }, 
    new { Id = 2, Flagged = false }, 
    new { Id = 3, Flagged = false }, 
    new { Id = 4, Flagged = true }, 
    new { Id = 5, Flagged = false }, 
    new { Id = 6, Flagged = false }, 
    new { Id = 7, Flagged = false }, 
    new { Id = 8, Flagged = true }, 
    new { Id = 9, Flagged = false } 
}; 

आप लिख सकते हैं:

var grouped = 
    from i in arrayOfElements 
    where i.Flagged 
    select 
     (new[] { i.Id }) 
     .Union(arrayOfElements.Where(i2 => i2.Id > i.Id).TakeWhile(i2 => !i2.Flagged).Select(i2 => i2.Id)) 
     .ToArray(); 

यह काम करता है अगर आपके तत्व ordere हैं आईडी विशेषता द्वारा डी। यदि वे नहीं करते हैं, तो आपको अपनी मूल सरणी पर अनुक्रमित करना होगा, जो कि linq के साथ भी करना आसान होना चाहिए, इसलिए आपको अनुक्रम मिलेगा।

इसके अलावा, एक बेहतर विकल्प होना चाहिए:

// for each flagged element, slice the array, 
// starting on the flagged element until the next flagged element 
var grouped = 
    from i in arrayOfElements 
    where i.Flagged 
    select 
     arrayOfElements 
      .SkipWhile(i2 => i2 != i) 
      .TakeWhile(i2 => i2 == i || !i2.Flagged) 
      .Select(i2 => i2.Id) 
      .ToArray(); 

ध्यान दें कि उन जवाब शुद्ध LINQ का उपयोग कर रहे हैं।

+0

मुझे लिंक बहुत पसंद है, और जहां मैं कर सकता हूं इसका उपयोग कर सकता हूं, लेकिन आपको नहीं लगता कि यह शास्त्रीय दृष्टिकोण की तुलना में बदसूरत है (मेरी पोस्ट देखें)? मुझे यह भी लगता है कि यह प्रदर्शन में बदतर होने जा रहा है। –

+0

मैंने आईडी द्वारा आदेश देने की आवश्यकता को दूर करने के लिए इसे थोड़ा सा विकसित किया। लेकिन मैं सहमत हूं, यह प्रदर्शन पर भी बदतर है। मुझे @LBushkin उत्तर पसंद आया, जिसके लिए एक नई विस्तार विधि तैयार की गई। –

+0

+1 मुझे आपका दूसरा विकल्प पसंद है। यह देखने में अच्छा लगा कि शुद्ध लिनक के साथ कितने काम किए जा सकते हैं, कि मैंने linq में करने के बारे में भी सोचा नहीं होगा। –

5

मैं इस के साथ एक समान समस्या थी, और GroupBy और बंद का उपयोग कर इसे हल।

//sample data 
var arrayOfElements = new[] { 
    new { Id = 1, Flagged = true }, 
    new { Id = 2, Flagged = false }, 
    new { Id = 3, Flagged = false }, 
    new { Id = 4, Flagged = true }, 
    new { Id = 5, Flagged = false }, 
    new { Id = 6, Flagged = false }, 
    new { Id = 7, Flagged = false }, 
    new { Id = 8, Flagged = true }, 
    new { Id = 9, Flagged = false } 
}; 

//this is the closure which will increase each time I see a flagged 
int flagCounter = 0; 

var query = 
    arrayOfElements.GroupBy(e => 
     { 
      if (e.Flagged) 
       flagCounter++; 
      return flagCounter; 
     }); 

यह क्या करता है एक पूर्णांक (flagCounter) है, जो हर बार एक फ्लैग किए गए तत्व पाया जाता है बढ़ जाती है पर समूहीकरण है।
कृपया ध्यान दें कि यह AsParallel() के साथ काम नहीं करेगा।

परीक्षण के परिणाम:

foreach(var group in query) 
{ 
    Console.Write("\r\nGroup: "); 
    foreach (var element in group) 
     Console.Write(element.Id); 
} 

आउटपुट:

समूह: 123
समूह: 4567
समूह: 89

+0

बहुत ही रोचक दृष्टिकोण। अन्य समाधानों के लिए बहुत अलग है, लेकिन आप जानते हैं, कई सड़कों पर रोम की ओर जाता है ... –

+0

दिलचस्प है, मुझे लगता है कि यह क्वेरी के लिए राज्य को पेश करने के लिए "हैकिश" है। अंतिम परिणाम, हालांकि, अच्छा है (एक linq समूह)। हो सकता है कि इसे बंद करने के लिए एक तरीका है? –

+0

@ फ़ैबियो: क्लोजर सी # के लिए एक बढ़िया जोड़ा है, और प्रतिनिधियों (जैसे इस मामले में) में उपयोग के लिए सबसे उपयुक्त हैं। मेरा ज्ञान मुझे अभी तक उनका उपयोग करके थ्रेड-सुरक्षित समाधान में नहीं ला सकता है, लेकिन निश्चित रूप से यह कोड निष्पादक और रखरखाव के रूप में पठनीय है। –

संबंधित मुद्दे