2017-10-15 14 views
0

J'ai une liste des id (int) commeObtenez des plages possibles de collection int

[1,2,3,5,7,8,11,13,14..] 

est-il un moyen rapide/smart (LINQ?) Pour obtenir tous les ids ou si possible, les plages? Le résultat devrait être comme [1-3, 5, 7-8, 11, 13-14].

Bien sûr, il est facile de boucler et compter la valeur int pour obtenir le résultat, mais je suis sûr qu'il doit y avoir un moyen plus facile de le faire.

+0

quelle est la logique pour générer plage? pourquoi pas 1-5, 3-8 etc? – Sajeetharan

+0

_y at-il un moyen rapide/intelligent (linq?) _ - si par "rapide" vous voulez dire vitesse d'exécution, alors "foreach" sera toujours plus rapide que LINQ – Fabio

Répondre

0

Vieille école avec une boucle unique.

try 
{ 
    List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 }; 
    int istart = i[0]; 
    bool flag = false; 
    // Use StringBuilder 
    for(int index = 0;index<i.Count-1;index++) 
    { 
     if ((i[index] + 1) == i[index + 1]) 
     { 
       flag = true; 
       continue; 
     } 
     else 
     { 
       if (!flag) 
        Console.Write(istart); 
       else 
        Console.Write(istart + "-" + i[index]); 

       Console.Write(","); 

       flag = false; 
       istart = i[index + 1]; 
      } 
     } 
     if (istart + 1 == i[i.Count - 1]) 
      Console.Write(istart + "-" + i[i.Count - 1]); 
     else 
      Console.WriteLine(istart); 
} 
catch(Exception ex) 
{ 
     Console.WriteLine(ex.Message); 
} 
Console.WriteLine(); 
Console.WriteLine("Done"); 
Console.Read(); 

entrée List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };

enter image description here

entrée List<int> i = new List<int>() { 1, 2, 3, 4, 5, 7, 8, 11, 13, 13 };

enter image description here

entrée List<int> i = new List<int>() { 1, 4, 5, 7, 8, 9, 2, 13, 15, 17 };

enter image description here

0

Il y a une façon LINQ de le faire (si le nombre ne répètent pas), mais je ne sais pas s'il est plus facile:

int last = -1; 
int rank = 0; 
IEnumerable<string> grouped = arr 
    .GroupBy(i => 
    { 
     rank += i - last - 1; 
     last = i; 
     return rank; 
    }) 
    .Select(g => g.Count() == 1 ? g.First().ToString() 
       : g.First().ToString() + "-" + g.Last().ToString()); 

Il semble assez compliqué pour moi, plus de ressources que nécessaire et pas flexible. Linq est génial pour de nombreux cas, mais parfois ça ne va pas très bien. Boucle simple est parfois la meilleure que vous pouvez obtenir:

IEnumerable<string> Group(IEnumerable<int> sortedArr) 
{ 
    using (var en = sortedArr.GetEnumerator()) 
    { 
     if (!en.MoveNext()) 
     { 
      yield break; 
     } 
     int first = en.Current; 
     int last = first; 
     int count = 1; 

     while (true) 
     { 
      bool end; 
      if ((end = !en.MoveNext()) || en.Current - last > 1) 
      { 
       if (count == 1) 
       { 
        yield return first.ToString(); 
       } 
       //else if (count == 2) 
       //{ 
       // yield return first.ToString(); 
       // yield return last.ToString(); 
       //} 
       else 
       { 
        yield return first.ToString() + "-" + last.ToString(); 
       } 
       if (end) { yield break; } 
       first = en.Current; 
       count = 1; 
      } 
      else 
      { 
       ++count; 
      } 
      last = en.Current; 
     } 
    } 
} 

Repères

permet de mesurer comment (in) efficace LINQ est en fait ici sur la matrice de 10M:

simple loop: 328MB, 1.2sec 
     my linq: 790MB, 2.7sec 
Fredous linq: 1100MB, 7days (estimated) 

le code:

int size = 10000000; 
int[] arr = new int[size]; 
Random rnd = new Random(1); 

arr[0] = 0; 
for(int i = 1; i < size; ++i) 
{ 
    if (rnd.Next(100) < 25) 
    { 
     arr[i] = arr[i - 1] + 2; 
    } 
    else 
    { 
     arr[i] = arr[i - 1] + 1; 
    } 
}   

System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch(); 
st.Start(); 
var res = Group(arr).ToList(); 
st.Stop(); 

MessageBox.Show(st.ElapsedMilliseconds.ToString()); 
MessageBox.Show(res.Sum(s => s.Length).ToString());// to be sure the work is done 
+0

vous devriez mettre le code de référence réel dans la réponse aussi ;-) – Fredou

+0

@Fredou ok, ajouté –

+0

J'ai ajouté un petit avertissement sur ma réponse ;-) – Fredou

1

Avertissementce qui est très lent sur grande liste, vous devriez faire une distincte trop

cela devrait faire l'affaire

static void Main(string[] args) 
    { 
     //a list with a possible of duplicate 
     var theList = (new int[] { 1, 2, 3, 5, 7, 8, 11, 13, 14, 13 }).OrderBy(x => x).ToList(); 

     var step1 = theList.Select((a, b) => theList.Skip(b).TakeWhile((x, y) => a == x || theList[b + y] - 1 == theList[b + y - 1])); 

     var step2 = step1.GroupBy(x => x.Last()) 
         .Select(x => x.SelectMany(y => y).Distinct()) 
         .Select(x => x.Count() > 1 ? string.Format("{0}-{1}", x.First(), x.Last()) : x.First().ToString()); 


     var result = string.Format("[{0}]", string.Join(", ", step2)); 

    } 
0

Compte tenu d'une liste ordonnée et ids unique, je pense que l'approche la plus simple est d'utiliser classique pour et tout

List<int> ids = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 }; 
       int i = 0; 
       bool isrange; 

       for(i=0;i<ids.Count;i++) 
       { 
        isrange = false; 
        Console.Write(ids[i]); 

        while (i < ids.Count-1 && ids[i + 1] == ids[i] + 1) 
        { 
         i++; 
         isrange = true; 
        } 

        if (isrange) 
         Console.Write("-" + ids[i]); 

        if (!(i + 1 == ids.Count)) 
         Console.Write(","); 
       }