Explicación del algoritmo agregado LINQ


Esto puede sonar cojo, pero no he sido capaz de encontrar una explicación realmente buena de Aggregate.

Bueno significa corto, descriptivo, comprensivo con un ejemplo pequeño y claro.

 603
Author: Jamiec, 2011-08-18

11 answers

La definición más fácil de entender de Aggregate es que realiza una operación en cada elemento de la lista teniendo en cuenta las operaciones que se han realizado antes. Es decir, realiza la acción sobre el primer y segundo elemento y lleva el resultado hacia adelante. Luego opera sobre el resultado anterior y el tercer elemento y continúa. sucesivamente.

Ejemplo 1. Sumando 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 añade 1 y 2 para hacer 3. Luego añade 3 (resultado de 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 para hacer a,b. Luego concatena a,b con una coma y c para hacer a,b,c. y así sucesivamente.

Ejemplo 3. Multiplicar números usando una semilla

Para completar, hay un sobrecarga de Aggregate que 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 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


Adición

El ejemplo 2, anterior, usa la concatenación de cadenas para crear una lista de valores separados por una coma. Esta es una manera simplista de explicar el uso de Aggregate que era la intención de esta respuesta. Sin embargo, si se utiliza esta técnica para crear realmente una gran cantidad de datos separados por comas, sería más apropiado usar un StringBuilder, y esto es totalmente compatible con Aggregate utilizando el 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

 881
Author: Jamiec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-06 02:10:24

Depende en parte de la sobrecarga de la que estás hablando, pero la idea básica es:{[11]]}

  • Comience con una semilla como el"valor actual"
  • Itera sobre la secuencia. Para cada valor en la secuencia:
    • Aplique una función especificada por el usuario para transformar (currentValue, sequenceValue) en (nextValue)
    • Conjunto currentValue = nextValue
  • Devuelve el final currentValue

Usted puede encontrar la Aggregate publicar en mi serie Edulinq útil-incluye una más detallada descripción (incluyendo las diversas sobrecargas) e implementaciones.

Un ejemplo simple es usar Aggregate como una 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 sumando todas las longitudes de cadenas en una secuencia de cadenas:

int total = sequence.Aggregate(0, (current, item) => current + item.Length);

Personalmente, rara vez encuentro Aggregate útil - los métodos de agregación "personalizados" generalmente son lo suficientemente buenos para mí.

 117
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-16 15:08:24

Super corto El agregado funciona como fold en Haskell / ML / F#.

Un poco más largo .Max(), .Min(), .Resumir(), .Average () todo itera sobre los elementos en una secuencia y los agrega usando la función agregada respectiva. .Aggregate () es un agregador generalizado que permite al desarrollador especificar el estado de inicio (también conocido como seed) y la función aggregate.

Sé que pediste una breve explicación pero pensé que como otros dieron un par de cortos respuestas Me imaginé que quizás le interesaría una un poco más larga

Versión larga con código Una forma de ilustrar lo que hace podría ser mostrar cómo implementar Desviación Estándar de muestra una vez usando foreach y una vez usando .Agregado. Nota: No he priorizado el rendimiento aquí, así que itero varias veces sobre el colleciton innecesariamente

Primero una función auxiliar utilizada para crear una suma de distancias:

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));
}

Entonces una vez usando .Agregado:

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 por 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)
        );

Y qué.Agregado hace es que encapsula este patrón agregador y espero que la implementación de .El agregado se vería algo como esto:

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;
}

Usando las funciones de desviación estándar se vería algo como esto:

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);

IMHO

También.¿Legibilidad de ayuda agregada? En general me encanta LINQ porque creo .Donde, .Seleccionar, .OrderBy y así sucesivamente ayuda en gran medida legibilidad (si se evita hierarhical inlined .Seleccionar). Aggregate tiene que estar en Linq por razones de integridad, pero personalmente no estoy tan convencido de eso .Aggregate añade legibilidad en comparación con un foreach bien escrito.

 56
Author: Just another metaprogrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-18 10:39:39

Una imagen vale más que mil palabras

Recordatorio: Func<A, B, C> es una función con dos entradas de tipo A y B, que devuelve un C.

Enumerable.El agregado tiene tres sobrecargas:


Sobrecarga 1:

A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)

Agregado1

Ejemplo:

new[]{1,2,3,4}.Aggregate((x, y) => x + y);  // 10


Esta sobrecarga es simple, pero tiene las siguientes limitaciones: {[15]]}

  • la secuencia debe contener al menos una elemento,
    de lo contrario, la función lanzará un InvalidOperationException.
  • los elementos y el resultado deben ser del mismo tipo.



Sobrecarga 2:

B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)

Agregado2

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 de semilla (bIn).
  • la colección puede estar vacía,
    en este caso, la función producirá el valor de semilla como resultado.
  • los elementos y el resultado pueden tener diferentes tipos.



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 forma 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 blogpost.

 21
Author: 3dGrabber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-08 09:23:53

Aggregate se usa básicamente para Agrupar o Resumir datos.

Según MSDN "La Función agregada Aplica una función acumuladora sobre una secuencia."

Ejemplo 1: Agregue 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 colección. es decir: el valor inicial de la variable total será 1 por defecto.

Explicación variable

Total: mantendrá el valor de suma (agregado valor) devuelto por el func.

NextValue: es el siguiente valor en la secuencia del array. Este valor se añade al valor agregado, es decir, al total.

Ejemplo 2: Agregue todos los elementos en una matriz. También establezca el valor inicial del acumulador para comenzar a agregar con from 10.

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);

Argumentos explicación:

El primer argumento es la inicial(valor inicial, es decir, valor semilla) que se utilizará para iniciar la adición con el siguiente valor en la matriz.

El segundo argumento es un func que es un func que toma 2 int.

1.total: 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 del array. Este valor se añade al valor agregado, es decir, al total.

También depurar este código le dará una mejor comprensión de cómo funciona el agregado.

 14
Author: maxspan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-19 01:29:12

Aprendí mucho de la respuesta de Jamiec.

Si la única necesidad es generar una cadena CSV, puede probar esto.

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 es aquí

 6
Author: Rm558,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:26

Además de todas las grandes respuestas aquí ya, también lo he utilizado para caminar un elemento a través de una serie de pasos de transformación.

Si una transformación se implementa como Func<T,T>, puede agregar varias transformaciones a un List<Func<T,T>> y usar Aggregate para recorrer una instancia de T a través de cada paso.

Un ejemplo más concreto

Desea tomar un valor string y recorrerlo a través de una serie de transformaciones de texto que se podrían construir programáticamente.

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 los espacios iniciales y finales -> eliminar el primer carácter -> eliminar el último carácter -> convertir a mayúsculas. Los pasos de esta cadena se pueden agregar, eliminar o reordenar según sea necesario, para crear cualquier tipo de canalización de transformación que se requiera.

El resultado final de esta tubería específica, es que " cat " se convierte en "A".


Esto puede llegar a ser 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;

 3
Author: Bradley Uffner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-08 20:19:35

Una definición corta y esencial podría ser la siguiente: el método de extensión agregada 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 aún no es recursiva.

De esta manera se puede calcular el factorial de números, o concatenar cadenas.

 0
Author: Ciro Corvino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-12 08:54:16

Esta es una explicación sobre el uso de Aggregate en una API fluida 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 ordenación que tome un conjunto de campos, esto es muy fácil usando Aggregate en lugar de un bucle for, como esto:

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);
 0
Author: Jaider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-17 17:52:05

Agregado utilizado para sumar columnas en una matriz de enteros multidimensionales

        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 del func 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 trues 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
 0
Author: Dan M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-28 20:16:25

Todos han dado su explicación. Mi explicación es así.

El método Aggregate aplica una función a cada elemento de una colección. Por ejemplo, vamos a tener 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 llamado 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; }
 0
Author: user2983359,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-16 13:49:42