Esto puede sonar cojo, pero no he podido encontrar una explicación realmente buena Aggregate
.
Bueno significa breve, descriptivo, integral con un ejemplo pequeño y claro.
Esto puede sonar cojo, pero no he podido encontrar una explicación realmente buena Aggregate
.
Bueno significa breve, descriptivo, integral con un ejemplo pequeño y claro.
Respuestas:
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 en el primer y segundo elemento y lleva el resultado hacia adelante. 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 agrega 1
y 2
hacer 3
. Luego agrega 3
(resultado del anterior) y 3
(siguiente elemento en secuencia) para hacer 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 la misma manera. Concatenar a
una coma y b
hacer a,b
. Luego concatena a,b
con una coma y c
hacer a,b,c
. y así.
Ejemplo 3. Multiplicar números usando una semilla
Para completar, hay una sobrecarga de la Aggregate
cual toma un valor semilla.
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 lleva adelante y se multiplica por el siguiente número en la secuencia 20
para dar un resultado 1000
. Esto continúa a través de los 2 elementos restantes de la secuencia.
Ejemplos en vivo: http://rextester.com/ZXZ64749
Documentos: http://msdn.microsoft.com/en-us/library/bb548651.aspx
Apéndice
El ejemplo 2 anterior usa la concatenación de cadenas para crear una lista de valores separados por una coma. Esta es una forma simplista de explicar el uso de Aggregate
cuál fue la intención de esta respuesta. Sin embargo, si usa esta técnica para crear una gran cantidad de datos separados por comas, sería más apropiado usar a StringBuilder
, y esto es totalmente compatible con el Aggregate
uso de la sobrecarga sembrada para iniciar el 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
TakeWhile
entonces un Aggregate
- eso es lo mejor de Enumerable extensiones - son fácilmente encadenables. Entonces terminas con TakeWhile(a => a == 'a').Aggregate(....)
. Vea este ejemplo: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(sin necesidad de agregados o constructores de cadenas), pero sí, sé que el punto de la respuesta era dar un ejemplo de uso de agregado, por lo que es genial. Pero aún quería mencionar que no se recomienda solo para unir cadenas, ya hay un método dedicado para eso ...
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Depende en parte de la sobrecarga de la que esté hablando, pero la idea básica es:
(currentValue, sequenceValue)
en(nextValue)
currentValue = nextValue
currentValue
Puede encontrar útil la Aggregate
publicación en mi serie Edulinq : incluye una descripción más detallada (incluidas las diversas sobrecargas) y las implementaciones.
Un ejemplo simple es usar 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 quizás sumando todas las longitudes de cadenas en una secuencia de cadenas:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Personalmente, rara vez me parece Aggregate
útil: los métodos de agregación "a medida" suelen ser lo suficientemente buenos para mí.
El agregado súper corto funciona como un pliegue en Haskell / ML / F #.
Un poco más largos .Max (), .Min (), .Sum (), .Average () todos iteran sobre los elementos en una secuencia y los agregan usando la función de agregado respectiva. .Aggregate () es un agregador generalizado en el sentido de que permite al desarrollador especificar el estado de inicio (también conocido como semilla) y la función de agregado.
Sé que pediste una breve explicación, pero pensé que mientras otros daban un par de respuestas cortas, pensé que tal vez te interesaría una más larga.
Versión larga con código Una forma de ilustrar qué puede hacer es mostrar cómo implementa la Desviación estándar de muestra una vez que usa foreach y una vez que usa .Aggregate. Nota: No he priorizado el rendimiento aquí, así que repito varias veces innecesariamente la recopilación.
Primero, una función auxiliar utilizada para crear una suma de distancias cuadráticas:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Luego, muestra la desviación estándar usando ForEach:
static double SampleStandardDeviation_ForEach (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
Luego, una vez que use .Agregar:
static double SampleStandardDeviation_Aggregate (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
Tenga en cuenta que estas funciones son idénticas, excepto cómo se calcula sumOfQuadraticDistance:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Versus:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Entonces, lo que hace .Aggregate es que encapsula este patrón de agregación y espero que la implementación de .Aggregate se vea más o menos así:
public static TAggregate Aggregate<TAggregate, TValue> (
this IEnumerable<TValue> values,
TAggregate seed,
Func<TAggregate, TValue, TAggregate> aggregator
)
{
var state = seed;
foreach (var value in values)
{
state = aggregator (state, value);
}
return state;
}
El uso de las funciones de desviación estándar se vería así:
var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
var average = ints.Average ();
var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate ();
var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach ();
Console.WriteLine (average);
Console.WriteLine (sampleStandardDeviation);
Console.WriteLine (sampleStandardDeviation2);
En mi humilde opinión
Entonces, ¿Agregado ayuda a la legibilidad? En general, me encanta LINQ porque creo que .Where, .Select, .OrderBy, etc., ayuda enormemente a la legibilidad (si evita las .selects jerárquicas en línea). Agregado tiene que estar en Linq por razones de integridad, pero personalmente no estoy tan convencido de que. Agregado agrega legibilidad en comparación con un foreach bien escrito.
SampleStandardDeviation_Aggregate()
y SampleStandardDeviation_ForEach()
no pueden ser private
(por defecto en ausencia de un calificador de acceso), por lo que debería haber sido acumulados por cualquiera public
o internal
, me parece
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>(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:
InvalidOperationException
.Sobrecarga 2:
B Aggregate<A, B>(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:
bIn
).Sobrecarga 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
La tercera sobrecarga no es muy útil IMO.
Lo mismo se puede escribir de manera más sucinta mediante el uso de sobrecarga 2 seguido de una función que transforma su resultado.
Las ilustraciones están adaptadas de este excelente blog .
Aggegate
en .net que toma un Func<T, T, T>
.
seed
, aplica la función de acumulador N -1 veces; mientras que las otras sobrecargas (que no toman una seed
) aplicar la función de acumulador de N veces.
El agregado se usa básicamente para agrupar o resumir datos.
De acuerdo con MSDN "La función de agregado aplica una función de acumulador sobre una secuencia".
Ejemplo 1: Agregar todos los números en una matriz.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* importante: el valor agregado inicial por defecto es el elemento 1 en la secuencia de la colección. es decir: el valor inicial de la variable total será 1 por defecto.
explicación variable
total: mantendrá el valor de suma (valor agregado) devuelto por la función.
nextValue: es el siguiente valor en la secuencia de la matriz. Este valor se agrega al valor agregado, es decir, total.
Ejemplo 2: Agregar todos los elementos en una matriz. Establezca también el valor inicial del acumulador para comenzar a agregar desde 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
explicación de argumentos:
El primer argumento es el inicial (valor inicial, es decir, valor inicial) que se utilizará para iniciar la adición con el siguiente valor en la matriz.
El segundo argumento es una función que es una función que requiere 2 int.
1.total: esto se mantendrá igual que antes del valor de suma (valor agregado) devuelto por el func después del cálculo.
2.nextValue:: es el siguiente valor en la secuencia de la matriz. Este valor se agrega al valor agregado, es decir, total.
Además, la depuración de este código le dará una mejor comprensión de cómo funciona el agregado.
Aprendí mucho de la respuesta de Jamiec .
Si la única necesidad es generar una cadena CSV, puede intentarlo.
var csv3 = string.Join(",",chars);
Aquí hay una prueba con 1 millón de cadenas
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
El código fuente está aquí.
Además de todas las excelentes respuestas aquí, también lo he usado para guiar un artículo a través de una serie de pasos de transformación.
Si una transformación se implementa como a Func<T,T>
, puede agregar varias transformaciones a ay List<Func<T,T>>
usar Aggregate
para recorrer una instancia de T
cada paso.
Desea tomar un string
valor y recorrerlo a través de una serie de transformaciones de texto que podrían construirse mediante programación.
var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());
var text = " cat ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);
Esto creará una cadena de transformaciones: eliminar espacios iniciales y finales -> eliminar el primer carácter -> eliminar el último carácter -> convertir a mayúsculas. Los pasos en esta cadena se pueden agregar, eliminar o reordenar según sea necesario, para crear cualquier tipo de tubería de transformación que se requiera.
El resultado final de esta tubería específica, es que se " cat "
convierte "A"
.
Esto puede volverse muy poderoso una vez que te das cuenta de que T
puede ser cualquier cosa . Esto podría usarse para transformaciones de imágenes, como filtros, usando BitMap
como ejemplo;
Definición
El método agregado es un método de extensión para colecciones genéricas. El método agregado aplica una función a cada elemento de una colección. No solo aplica una función, sino que toma su resultado como valor inicial para la próxima iteración. Por lo tanto, como resultado, obtendremos un valor calculado (min, max, avg u otro valor estadístico) de una colección.
Por lo tanto, el método Agregado es una forma de implementación segura de una función recursiva.
Seguro , porque la recursión iterará sobre cada elemento de una colección y no podemos obtener ninguna suspensión de bucle infinito por una condición de salida incorrecta. Recursivo , porque el resultado de la función actual se usa como parámetro para la próxima llamada a la función.
Sintaxis:
collection.Aggregate(seed, func, resultSelector);
Cómo funciona:
var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5
Uso práctico:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
que está haciendo lo mismo que esta función:
public static int Factorial(int n)
{
if (n < 1) return 1;
return n * Factorial(n - 1);
}
var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
var min = numbers.Aggregate((result, x) => (result < x)? result: x);
var path = @“c:\path-to-folder”;
string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);
File.WriteAllText(path + “summary.txt”, output, Encoding.Default);
Console.WriteLine(“Text files merged into: {0}”, output); //or other log info
Esta es una explicación sobre el uso Aggregate
en una API Fluent como Linq Sorting.
var list = new List<Student>();
var sorted = list
.OrderBy(s => s.LastName)
.ThenBy(s => s.FirstName)
.ThenBy(s => s.Age)
.ThenBy(s => s.Grading)
.ThenBy(s => s.TotalCourses);
y veamos que queremos implementar una función de clasificación que tome un conjunto de campos, esto es muy fácil de usar en Aggregate
lugar de un ciclo for, como este:
public static IOrderedEnumerable<Student> MySort(
this List<Student> list,
params Func<Student, object>[] fields)
{
var firstField = fields.First();
var otherFields = fields.Skip(1);
var init = list.OrderBy(firstField);
return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}
Y podemos usarlo así:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Todos han dado su explicación. Mi explicación es así.
El método agregado aplica una función a cada elemento de una colección. Por ejemplo, tengamos la colección {6, 2, 8, 3} y la función Agregar (operador +) que hace (((6 + 2) +8) +3) y devuelve 19
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19
En este ejemplo, se pasa el método con nombre Add en lugar de la expresión lambda.
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19
private static int Add(int x, int y) { return x + y; }
Una definición breve y esencial podría ser esta: el método de extensión Agregado de Linq permite declarar una especie de función recursiva aplicada a los elementos de una lista, cuyos operandos son dos: los elementos en el orden en que están presentes en la lista, un elemento a la vez, y el resultado de la iteración recursiva anterior o nada, si no es que aún, recursividad.
De esta forma, puede calcular el factorial de los números o concatenar cadenas.
Agregado utilizado para sumar columnas en una matriz entera multidimensional
int[][] nonMagicSquare =
{
new int[] { 3, 1, 7, 8 },
new int[] { 2, 4, 16, 5 },
new int[] { 11, 6, 12, 15 },
new int[] { 9, 13, 10, 14 }
};
IEnumerable<int> rowSums = nonMagicSquare
.Select(row => row.Sum());
IEnumerable<int> colSums = nonMagicSquare
.Aggregate(
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
);
Seleccionar con índice se utiliza dentro de la función Agregado para sumar las columnas coincidentes y devolver una nueva matriz; {3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13}.
Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42
Pero contar el número de verdades en una matriz booleana es más difícil ya que el tipo acumulado (int) difiere del tipo fuente (bool); aquí es necesaria una semilla para usar la segunda sobrecarga.
bool[][] booleanTable =
{
new bool[] { true, true, true, false },
new bool[] { false, false, false, true },
new bool[] { true, false, false, true },
new bool[] { true, true, false, false }
};
IEnumerable<int> rowCounts = booleanTable
.Select(row => row.Select(value => value ? 1 : 0).Sum());
IEnumerable<int> seed = new int[booleanTable.First().Length];
IEnumerable<int> colCounts = booleanTable
.Aggregate(seed,
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
);
Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2
[1,2,3,4]
será[3,3,4]
entonces[6,4]
y por fin[10]
. Pero en lugar de devolver una matriz de un solo valor, solo obtiene el valor en sí.