Agrupación ‘inteligente’ con LINQ

Tengo una lista de cadenas y quiero convertirla en algún tipo de lista agrupada, por lo que los valores se agruparían por su ubicación en la lista (no es una agrupación normal, pero de una manera, que los mismos elementos están solo en un grupo si están juntos). Considere el siguiente ejemplo:

LinkedList myList = new LinkedList(); myList.AddLast("aaa"); myList.AddLast("aaa"); myList.AddLast("bbb"); myList.AddLast("bbb"); myList.AddLast("aaa"); myList.AddLast("aaa"); myList.AddLast("aaa"); LinkedList groupedList = new LinkedList(); groupedList.AddLast(new MyTuple("aaa", 2)); groupedList.AddLast(new MyTuple("bbb", 2)); groupedList.AddLast(new MyTuple("aaa", 3)); 

¿Se puede realizar esta transformación con LINQ o debo escribir el algoritmo de manera habitual con bucles?

El método de extensión de esta respuesta hace prácticamente lo que pide (Microsoft también proporciona una implementación para agrupar elementos contiguos en una secuencia ):

 public static IEnumerable> GroupConsecutive(this IEnumerable set, Func predicate) { var i = 0; var k = 0; var ranges = from e in set let idx = ++i let next = set.ElementAtOrDefault(idx) let key = (predicate(e, next)) ? k : k++ group e by key into g select g; return ranges; } 

Podrías usarlo de la siguiente manera:

 void Main() { LinkedList myList = new LinkedList(); myList.AddLast("aaa"); myList.AddLast("aaa"); myList.AddLast("bbb"); myList.AddLast("bbb"); myList.AddLast("aaa"); myList.AddLast("aaa"); myList.AddLast("aaa"); IGrouping ggg; var groups=myList.GroupConsecutive((a,b)=>a==b); ILookup lookup=groups.ToLookup(g=>g.First(),g=>g.Count()); foreach(var x in lookup["aaa"]) { Console.WriteLine(x); //outputs 2 then 3 } foreach(var x in lookup["bbb"]) { Console.WriteLine(x); //outputs 2 } } 

Tenga en cuenta que el contenedor final es un ILookup que se comporta un poco como un Diccionario, pero permite almacenar varios valores en una sola clave.

Esto no es posible con el Diccionario. Un diccionario es asociativo (es decir: cada clave debe apuntar a una y solo una) y no está ordenada por naturaleza. Necesitarías usar algo más para esa estructura de datos. ¡No sería terriblemente difícil!

Editar

Una List> debería hacer el truco:

 List> structure = new List>(); structure.Add(new KeyValuePair(myList[0], 1); for(int i = 0; i < myList.Count; i++ ) { if( myList[i] == structure[structure.Count-1].Key ) { structure[structure.Count-1].Value += 1; } else { structure.Add(new KeyValuePair(myList[i], 1); } } 

Después de eso, debes (sin probar) tener lo que estás buscando.

Editar (un pensamiento más)

Si bien puede ser posible con linq (usando TakeWhile y cuentas …), todavía creo que tiene más sentido usar un bucle aquí, es simple. Alguien más shiny que yo podría intentar trabajar con Linq.