Con questo array int[]{ 1, 2, 3, 4, 7, 8, 11, 15,16,17,18 };
Come posso convertire a questo array di stringhe "1-4","7-8","11","15-18"
utilizzare LINQ per raggruppare una sequenza di numeri senza spazi
Suggerimenti? Linq?
Con questo array int[]{ 1, 2, 3, 4, 7, 8, 11, 15,16,17,18 };
Come posso convertire a questo array di stringhe "1-4","7-8","11","15-18"
utilizzare LINQ per raggruppare una sequenza di numeri senza spazi
Suggerimenti? Linq?
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;
}
}
}
}
Grazie, questo ha risparmiato un sacco di tempo. – nRk
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()
Voglio implementare quello che è più chiaro, stavo provando qualcosa con linq group ma senza successo – Alexandre
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.
_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
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);
}
}
è la sequenza sempre in ordine crescente? Possono esserci duplicati? – Ani