ILookup vs. IGrouping

He estado teniendo problemas para express las diferencias entre ILookup e IGrouping , y tengo curiosidad si ahora lo entiendo correctamente. LINQ IGrouping el problema al producir secuencias de elementos IGrouping al mismo tiempo que me daba un método de extensión ToLookup . Entonces sentí que eran lo mismo hasta que miré más de cerca.

 var q1 = from n in N group n by n.MyKey into g select g; // q1 is IEnumerable<IGrouping> 

Lo cual es equivalente a:

 var q2 = N.GroupBy(n => n.MyKey, n => n); // q2 is IEnumerable<IGrouping> 

Que se parece mucho a:

 var q3 = N.ToLookup(n => n.MyKey, n => n); // q3 is ILookup 

¿Estoy en lo correcto en las siguientes analogías?

  1. Un IGrouping es un único grupo (es decir, una secuencia con clave), análogo a KeyValuePair donde el valor es en realidad una secuencia de elementos (en lugar de un solo elemento)
  2. Un IEnumerable<IGrouping> es una secuencia de esos (similar a lo que obtienes al iterar sobre un IDictionary
  3. Un ILookup es más como un IDictionary donde el valor es en realidad una secuencia de elementos

Sí, todos esos son correctos.

Y ILookup también extiende IEnumerable> para que pueda iterar sobre todos los pares clave / colección, así como (o en lugar de) solo buscar claves particulares.

Básicamente creo que ILookup es como IDictionary> .

Tenga en cuenta que ToLookup es una operación “hágalo ahora” (ejecución inmediata) mientras que un GroupBy se difiere. Como sucede, con la forma en que “pull LINQ” funciona, cuando comienzas a sacar IGrouping s del resultado de un GroupBy , tiene que leer todos los datos de todos modos (porque no puedes cambiar de grupo a medio camino) mientras que en otras implementaciones puede ser capaz de producir un resultado de transmisión. (Lo hace en Push LINQ; esperaría que LINQ to Events sea el mismo).

Hay otra diferencia importante entre ILookup e IDictionary: el primero impone la inmutabilidad en el sentido de que no hay métodos para cambiar los datos (excepto cuando el consumidor realiza un lanzamiento explícito). Por el contrario, IDictionary tiene métodos como “Agregar” que permiten cambiar los datos. Entonces, desde la perspectiva de la progtwigción funcional y / o progtwigción paralela, ILookup es más agradable. (Ojalá también hubiera una versión de ILookup que asigna un solo valor a una clave en lugar de a un grupo).

(Por cierto, parece que vale la pena señalar que la relación entre IEnumerable e IList es similar a la de ILookup e IDictionary: la primera es inmutable, la última no).

GroupBy y ToLookUp tienen casi la misma funcionalidad EXCEPTO esto: Referencia

GroupBy: el operador GroupBy devuelve grupos de elementos en función de algún valor clave. Cada grupo está representado por IGrouping object.

ToLookup: ToLookup es lo mismo que GroupBy; la única diferencia es que se difiere la ejecución de GroupBy, mientras que la ejecución de ToLookup es inmediata.

Permite borrar la diferencia usando código de muestra. supongamos que tenemos una clase que representa el modelo de Person :

 class Personnel { public int Id { get; set; } public string FullName { get; set; } public int Level { get; set; } } 

después de eso definimos una lista de personnels según se personnels continuación:

  var personnels = new List { new Personnel { Id = 1, FullName = "P1", Level = 1 }, new Personnel { Id = 2, FullName = "P2", Level = 2 }, new Personnel { Id = 3, FullName = "P3", Level = 1 }, new Personnel { Id = 4, FullName = "P4", Level = 1 }, new Personnel { Id = 5, FullName = "P5", Level =2 }, new Personnel { Id = 6, FullName = "P6", Level = 2 }, new Personnel { Id = 7, FullName = "P7", Level = 2 } }; 

Ahora necesito agrupar personnels grupos por su nivel. Tengo dos enfoques aquí. usando GroupBy o ToLookUp . Si utilizo GroupBy , como se indicó anteriormente, utilizará la ejecución diferida, es decir, cuando itere a través de la colección, el siguiente elemento puede o no ser calculado hasta que se solicite.

  var groups = personnels.GroupBy(p => p.Level); personnels.RemoveAll(p => p.Level == 1); foreach (var product in groups) { Console.WriteLine(product.Key); foreach (var item in product) Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level); } 

En el código anterior, primero agrupé los elementos personnels , pero antes de iterarlos, eliminé algunos elementos personnels . Como GroupBy usa la ejecución diferida, el resultado final no incluirá los elementos eliminados, ya que la agrupación se computará aquí en el punto foreach .

Salida:

 2 2 >>> P2 >>> 2 5 >>> P5 >>> 2 6 >>> P6 >>> 2 7 >>> P7 >>> 2 

Pero si reescribo el código anterior como el siguiente: (tenga en cuenta que el código es el mismo que el código anterior, excepto GroupBy se reemplaza por ToLookUp )

  var groups = personnels.ToLookup(p => p.Level); personnels.RemoveAll(p => p.Level == 1); foreach (var product in groups) { Console.WriteLine(product.Key); foreach (var item in product) Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level); } 

Como ToLookUp usa ejecución inmediata, significa que cuando ToLookUp método ToLookUp , se genera el resultado y se aplica el grupo, por lo que si ToLookUp cualquier elemento de los personnels antes de la iteración, eso no afectará el resultado final.

Salida:

 1 1 >>> P1 >>> 1 3 >>> P3 >>> 1 4 >>> P4 >>> 1 2 2 >>> P2 >>> 2 5 >>> P5 >>> 2 6 >>> P6 >>> 2 7 >>> P7 >>> 2 

Nota: GroupBy y ToLookUp también devuelven diferentes tipos.

Puede usar ToDictionary en lugar de ToLookUp, pero debe prestar atención a esto 🙁 referencia )

El uso de ToLookup () es muy similar al de ToDictionary (), ambos le permiten especificar selectores de clave, selectores de valores y comparadores. La principal diferencia es que ToLookup () permite (y espera) las claves duplicadas, mientras que ToDictionary () no