Antes de continuar falando sobre Linq vou apresentar como ficariam os outros dois exemplos típicos de High-order functions seguindo a estrutura do Filter.
Map
Uma função que dada uma coleção de objetos de um tipo, retorna uma coleção de objetos de outro tipo, realizando a transformação.
Um exemplo é a integração entre dois aplicativos. É normal que entidades comuns, como Cliente, co-existam nas duas aplicações. Também é normal que esta entidade seja exatamente igual nos dois aplicativos. Daí a necessidade de transformações quando os sistemas estiverem trocando informações, desde formato de data, até normalização de tabelas.
Neste caso, o cliente em um dos aplicativos tem nome e sobrenome, já no outro, existe somente o nome completo. Ao trocar informações esta transformação é necessária.
private List<Cliente> MapClientes(List<ClienteCrm> collection)
{
List<Cliente> result = new List<Cliente>();
foreach (var item in collection)
{
result.Add(new Cliente(string.Format("{0} {1}", item.FirstName, item.LastName)));
}
return result;
}
Não é necessário seguir passo-a-passo, pois já fizemos isto no primeiro post, com o Filter. Isolando as partes que não mudam, das mutáveis, tenho o seguinte método para mapear qualquer par de objetos:
public delegate TResult Transform<TSource, TResult>(TSource source);
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> collection,
Transform<TSource, TResult> transform)
{
List<TResult> result = new List<TResult>();
foreach (var item in collection)
{
result.Add(transform(item));
}
return result;
}
O código que utiliza este método seria:
Cliente MapClienteCrmToCliente(ClienteCrm clienteCrm)
{
return new Cliente(string.Format("{0} {1}",
clienteCrm.FirstName,
clienteCrm.LastName));
}
List<Cliente> clientes = (List<Cliente>) clientesCrm.Map<ClienteCrm, Cliente>(MapClienteCrmToCliente);
Fold ou Reduce
Uma função que processa uma coleção de objetos retornando um valor. Este processamento de alguma forma combina os objetos desta coleção para calcular o resultado final. A mais óbvia seria a somatória, dada uma lista de inteiros retorne a soma destes.
Outro exemplo simples, mas um pouco menos óbvio, seria calcular o valor total de uma venda, que é a soma dos valores totais de cada uma de suas linhas, que é a quantidade de produtos multiplicada pelo preço unitário. O código ficaria assim:
public class SalesOrder
{
private List<SalesLine> lines = new List<SalesLine>();
public IEnumerable<SalesLine> Lines
{
get
{
return lines;
}
}
public double TotalAmount()
{
double total = 0;
foreach (var item in lines)
{
total = total + (item.Quantidade * item.Preco);
}
return total;
}
}
Seguindo o mesmo processo de sempre, eu teria um método Fold genérico:
public delegate TResult Acumulate<TSource, TResult>(TSource item, TResult initial);
public static TResult Fold<TSource, TResult>(this IEnumerable<TSource> collection, TResult initial, Acumulate<TSource, TResult> acumulate)
{
TResult result = initial;
foreach (var item in collection)
result = acumulate(item, result);
return result;
}
E sua utilização seria:
public double TotalAmount()
{
return lines.Fold<SalesLine, double>(0d, AcumulaTotalDaLinha);
}
private static double AcumulaTotalDaLinha(SalesLine item, double total)
{
return total + (item.Quantidade * item.Preco);
}
No final das contas esta seria a classe completa, com os três extension methods.
using System;
using System.Collections.Generic;
using System.Linq;
public static class HighOrderExtension
{
public static IEnumerable<T> Filter<T>(this IEnumerable<T> collection,
Predicate<T> filter)
{
var result = from item in collection
where filter(item)
select item;
return result.ToList<T>();
}
public delegate TResult Transform<TSource, TResult>(TSource source);
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> collection,
Transform<TSource, TResult> transform)
{
List<TResult> result = new List<TResult>();
foreach (var item in collection)
{
result.Add(transform(item));
}
return result;
}
public delegate TResult Acumulate<TSource, TResult>(TSource item, TResult initial);
public static TResult Fold<TSource, TResult>(this IEnumerable<TSource> collection, TResult initial, Acumulate<TSource, TResult> acumulate)
{
TResult result = initial;
foreach (var item in collection)
result = acumulate(item, result);
return result;
}
}
Até a próxima.