Algoritmo agregado de LINQ explicado
Esto puede parecer poco convincente, pero no he podido encontrar una explicación realmente buena Aggregate
.
Bueno significa breve, descriptivo, completo con un ejemplo pequeño y claro.
La definición más fácil de entender Aggregate
es que realiza una operación en cada elemento de la lista teniendo en cuenta las operaciones anteriores. Es decir, realiza la acción sobre el primer y segundo elemento y traslada el resultado. Luego opera sobre el resultado anterior y el tercer elemento y continúa. etc.
Ejemplo 1. Sumar números
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
Esto suma 1
y 2
para hacer 3
. Luego agrega 3
(resultado del anterior) y 3
(siguiente elemento en secuencia) para crear 6
. Luego agrega 6
y 4
para hacer 10
.
Ejemplo 2. crear un csv a partir de una matriz de cadenas
var chars = new []{"a","b","c","d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Esto funciona de manera muy similar. Concatenar a
una coma y b
hacer a,b
. Luego concatena a,b
con una coma y c
para hacer a,b,c
. etcétera.
Ejemplo 3. Multiplicar números usando una semilla
Para completar, hay una sobrecarga que Aggregate
toma un valor inicial.
var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)
Al igual que los ejemplos anteriores, esto comienza con un valor de 5
y lo multiplica por el primer elemento de la secuencia 10
dando un resultado de 50
. Este resultado se traslada y se multiplica por el siguiente número de la secuencia 20
para dar un resultado de 1000
. Esto continúa a través de los 2 elementos restantes de la secuencia.
Ejemplos en vivo: http://rextester.com/ZXZ64749
Docs: http://msdn.microsoft.com/en-us/library/bb548651.aspx
Apéndice
El ejemplo 2 anterior utiliza la concatenación de cadenas para crear una lista de valores separados por una coma. Esta es una forma simplista de explicar el uso que Aggregate
fue la intención de esta respuesta. Sin embargo, si se utiliza esta técnica para crear una gran cantidad de datos separados por comas, sería más apropiado utilizar un archivo StringBuilder
, y esto es totalmente compatible con Aggregate
el uso de la sobrecarga inicializada para iniciar el archivo StringBuilder
.
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
if(a.Length>0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
Ejemplo actualizado: http://rextester.com/YZCVXV6464
Depende en parte de qué sobrecarga estás hablando, pero la idea básica es:
- Comience con una semilla como "valor actual"
- Iterar sobre la secuencia. Para cada valor de la secuencia:
- Aplicar una función especificada por el usuario para transformarse
(currentValue, sequenceValue)
en(nextValue)
- Colocar
currentValue = nextValue
- Aplicar una función especificada por el usuario para transformarse
- devolver la final
currentValue
Puede que la Aggregate
publicación de mi serie Edulinq le resulte útil: incluye una descripción más detallada (incluidas las diversas sobrecargas) e implementaciones.
Un ejemplo sencillo es utilizar Aggregate
como alternativa a Count
:
// 0 is the seed, and for each item, we effectively increment the current value.
// In this case we can ignore "item" itself.
int count = sequence.Aggregate(0, (current, item) => current + 1);
O tal vez sumar todas las longitudes de las cadenas en una secuencia de cadenas:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Personalmente, rara vez lo encuentro Aggregate
útil: los métodos de agregación "adaptados" suelen ser lo suficientemente buenos para mí.
Una imagen vale mas que mil palabras
Recordatorio:
Func<X, Y, R>
es una función con dos entradas de tipoX
yY
, que devuelve un resultado de tipoR
.
Enumerable.Aggregate tiene tres sobrecargas:
Sobrecarga 1:
A Aggregate<A>(this IEnumerable<A> a, Func<A, A, A> f)
Ejemplo:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
Esta sobrecarga es simple, pero tiene las siguientes limitaciones:
- la secuencia debe contener al menos un elemento;
de lo contrario, la función arrojará un archivoInvalidOperationException
. - Los elementos y el resultado deben ser del mismo tipo.
Sobrecarga 2:
B Aggregate<A, B>(this IEnumerable<A> a, B bIn, Func<B, A, B> f)
Ejemplo:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
Esta sobrecarga es más general:
- se debe proporcionar un valor inicial (
bIn
). - la colección puede estar vacía;
en este caso, la función arrojará el valor inicial como resultado. - Los elementos y el resultado pueden tener diferentes tipos.
Sobrecarga 3:
C Aggregate<A,B,C>(this IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
La tercera sobrecarga no es muy útil en mi opinión.
Lo mismo se puede escribir de manera más sucinta usando la sobrecarga 2 seguida de una función que transforma su resultado.
Las ilustraciones están adaptadas de esta excelente publicación de blog .
Agregar se utiliza básicamente para agrupar o resumir datos.
Según MSDN, "La función agregada aplica una función acumuladora sobre una secuencia".
Ejemplo 1: sumar todos los números de una matriz.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
*importante: El valor agregado inicial de forma predeterminada es el primer elemento en la secuencia de recopilación. es decir: el valor inicial de la variable total será 1 de forma predeterminada.
explicación variable
total: contendrá el valor resumido (valor agregado) devuelto por la función.
nextValue: es el siguiente valor en la secuencia de la matriz. Este valor se suma al valor agregado, es decir, al total.
Ejemplo 2: agregar todos los elementos de una matriz. También establezca el valor inicial del acumulador para comenzar a sumar desde 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
explicación de los argumentos:
el primer argumento es el inicial (valor inicial, es decir, valor inicial) que se utilizará para comenzar la suma con el siguiente valor de la matriz.
el segundo argumento es una función que toma 2 int.
1.total: esto se mantendrá igual que antes del valor de suma (valor agregado) devuelto por la función después del cálculo.
2.nextValue:: es el siguiente valor en la secuencia de la matriz. Este valor se suma al valor agregado, es decir, al total.
Además, depurar este código le permitirá comprender mejor cómo funcionan los agregados.