2011-01-13 16 views

risposta

36
var array = new int[] { 1, 2, 3, 4, 7, 8, 11, 15, 16, 17, 18 }; 

var result = string.Join(",", array 
    .Distinct() 
    .OrderBy(x => x) 
    .GroupAdjacentBy((x, y) => x + 1 == y) 
    .Select(g => new int[] { g.First(), g.Last() }.Distinct()) 
    .Select(g => string.Join("-", g))); 

con

public static class LinqExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(
     this IEnumerable<T> source, Func<T, T, bool> predicate) 
    { 
     using (var e = source.GetEnumerator()) 
     { 
      if (e.MoveNext()) 
      { 
       var list = new List<T> { e.Current }; 
       var pred = e.Current; 
       while (e.MoveNext()) 
       { 
        if (predicate(pred, e.Current)) 
        { 
         list.Add(e.Current); 
        } 
        else 
        { 
         yield return list; 
         list = new List<T> { e.Current }; 
        } 
        pred = e.Current; 
       } 
       yield return list; 
      } 
     } 
    } 
} 
+1

Grazie, questo ha risparmiato un sacco di tempo. – nRk

1

Qual è l'algoritmo che si desidera implementare? Scopri cosa vuoi che accada, quindi verifica se può essere reso più chiaro con una traduzione LINQ. Ecco qualcosa di non LINQ che potrebbe darti un'idea.

int[] array = { 1, 2, 3, 4, 7, 8, 11, 15, 16, 17, 18}; 
List<string> ranges = new List<string>(); 

// code assumes array is not zero-length, is distinct, and is sorted. 
// to do: handle scenario as appropriate if assumptions not valid 

Action<int, int, List<string>> addToRanges = (first, last, list) => 
{ 
    if (last == first) 
     list.Add(last.ToString()); 
    else 
     list.Add(string.Format("{0}-{1}", first, last)); ; 
}; 

int firstItem = array[0]; 
int lastItem = firstItem; 
foreach (int item in array.Skip(1)) 
{ 
    if (item > lastItem + 1) 
    { 
     addToRanges(firstItem, lastItem, ranges); 
     firstItem = lastItem = item; 
    } 
    else 
    { 
     lastItem = item; 
    } 
} 

addToRanges(firstItem, lastItem, ranges); 

// return ranges or ranges.ToArray() 
+0

Voglio implementare quello che è più chiaro, stavo provando qualcosa con linq group ma senza successo – Alexandre

4

Non hai bisogno di Linq; infatti, la soluzione più semplice richiede la conoscenza di tre posizioni nell'array (il numero iniziale, il numero corrente e il numero successivo dopo l'attuale), per cui gli Enumerables non sono adatti.

Prova questo:

var start = 0; 
var end = 0; 
var write = false; 
var builder = new StringBuilder(); 
for(var i=0; i<array.Length; i++) 
{ 
    //arranged this way to avoid ArrayOutOfBoundException 
    //if the next index doesn't exist or isn't one greater than the current, 
    //the current index is the end of our incremental range. 
    if(i+1 == array.Length || array[i+1] > array[i] + 1) 
    { 
     end = i; 
     write = true; 
    } 

    if(write) 
    { 
     if(end - start == 0) //one number 
     builder.Append(String.Format("{0}, ", array[start]); 
     else //multi-number range 
     builder.Append(String.Format("{0}-{1}, ", array[start], array[end]); 

     start = i+1; 
     end = i+1; //not really necessary but avoids any possible case of counting backwards 
     write = false; 
    } 

} 

È possibile riorganizzare questo per ridurre la nidificazione di codice, continue nelle prime fasi del ciclo di logica, e rimuovere alcuni Vars; guadagnerai qualche millesimo di tempo di esecuzione. Dovrai anche tagliare gli ultimi due caratteri (una virgola e uno spazio finale) dalla fine dello StringBuilder prima di estrarre la stringa.

+0

_Non hai bisogno di Linq_? Mentre la tua versione è totalmente a posto, una soluzione LINQ ideomatica ha anche il vantaggio di poter facilmente portare una tale soluzione a qualsiasi linguaggio di programmazione funzionale, come F # o Haskell. – mbx

1

Ecco un taglio a questo:

public static IEnumerable<string> ToRanges(this IEnumerable<int> values) 
{ 
    int? start = null, end = null; 
    foreach (var value in values.OrderBy(vv => vv)) 
    { 
     if (!start.HasValue) 
     { 
      start = value; 
     } 
     else if (value == (end ?? start) + 1) 
     { 
      end = value; 
     } 
     else 
     { 
      yield return end.HasValue 
       ? String.Format("{0}-{1}", start, end) 
       : String.Format("{0}", start); 
      start = value; 
      end = null; 
     } 
    } 

    if (start.HasValue) 
    { 
     yield return end.HasValue 
      ? String.Format("{0}-{1}", start, end) 
      : String.Format("{0}", start); 
    } 
} 
Problemi correlati