Wednesday, 4 May 2011

C# Functional programming

namespace Test
{
    public static class Extensions
    {
        //ForEach IEnumerable extension
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            foreach (var item in source)
            {
                action(item);
            }
        }
    }
    class Person
    {
        public string Name { get; set; }
        public string City { get; set; }
    }
    class Account
    {
        public string Name { get; set; }
        public string AccountName { get; set; }
        public double Amount { get; set; }
        public List<string> Cards { get; set; }
    }
    [TestFixture]
    public class TestFunc
    {
        private List<Person> people;
        private List<Account> accounts;
        [TestFixtureSetUp]
        public void SetUp()
        {
            people = new List<Person>();
            people.Add(new Person(){Name="John", City="NY"});
            people.Add(new Person() { Name = "Tom", City = "London" });
            accounts = new List<Account>();
            accounts.Add(new Account() { Name = "John", Amount = 1, AccountName = "Savings", Cards = new List<string>(){"Amex","Visa"}});
            accounts.Add(new Account() { Name = "Tom", Amount = 2, AccountName = "Current", Cards = new List<string>() { "Amex" } });
            accounts.Add(new Account() { Name = "John", Amount = 3, AccountName = "Savings", Cards = new List<string>() { "Mastercard" } });
            accounts.Add(new Account() { Name = "Tom", Amount = 4, AccountName = "Current", Cards = new List<string>() { "Mastercard", "Visa" } });
            accounts.Add(new Account() { Name = "John", Amount = 5, AccountName = "Mortgage", Cards = new List<string>() { "Visa" } });            
        }
        [Test]
        public void TestLinq()
        {            
            Console.WriteLine("Total Amount in All acounts:\n{0}", accounts.Sum(item => item.Amount));
            Console.WriteLine("\nTotal Amount in John's accounts:\n{0}", accounts.Where(x => x.Name.Equals("John")).Sum(item => item.Amount));
            Console.WriteLine("\nSum using Aggregate function:\n{0}", accounts.Select((x) => x.Amount).Aggregate((double)10, (x, y) => x + y));
            Console.WriteLine("\nMultiply using Aggregate function:\n{0}", accounts.Select((x) => x.Amount).Aggregate((x, y) => x * y));            
            Console.WriteLine("\nIterate John's accounts:");
            accounts.Where(x => x.Name.Equals("John")).ForEach((x) => Console.Write("{0},",x.Amount));
            Console.WriteLine("\n\nIterate John's accounts (with transform)");
            accounts.Where(x => x.Name.Equals("John")).Select((x)=>x.Amount).ForEach(Console.WriteLine);
            Console.WriteLine("\nMaping to new anonymous class:");
            accounts.Select((x) => new { Upper = x.Name.ToUpper(), Lower =x.Name.ToLower()}).ForEach((x) => Console.WriteLine("{0}", x.Upper));
            Console.WriteLine("\nMaping to new anonymous class (with index):");
            accounts.Select((x, index) => new { Index = index, Upper = x.Name.ToUpper() }).ForEach((x) => Console.WriteLine("{0}->{1}", x.Index, x.Upper));
            
            Console.WriteLine("\nFlatten Children (single loop):");
            accounts.SelectMany(x => x.Cards).ForEach(Console.WriteLine);  
            
            Console.WriteLine("\nConcat two lists:");
            accounts.Select(x => x.Name).Concat(accounts.SelectMany(x=>x.Cards)).ForEach(Console.WriteLine);
            
            Console.WriteLine("\nGroupBy with Count:");
            accounts.GroupBy(x => x.Name, x => x.Amount).ToList().Select((x) => new { Key = x.Key, Count = x.Count() }).ForEach((x) => Console.WriteLine("{0}->{1}", x.Key, x.Count));
            Console.WriteLine("\nConcat with Sum:");
            accounts.GroupBy(x => x.Name, x => x.Amount).ToList().Select((x) => new { Key = x.Key, Sum = x.Sum() }).ForEach((x) => Console.WriteLine("{0}->{1}", x.Key, x.Sum));
            
            Console.WriteLine("\nTake all values until condition met:");
            accounts.TakeWhile(x => x.Amount < 3).ForEach((x) => Console.WriteLine("{0}->{1}", x.Name, x.Amount));
            Console.WriteLine("\nTake all values from condition met:");
            accounts.SkipWhile(x => x.Amount < 4).ForEach((x) => Console.WriteLine("{0}->{1}", x.Name, x.Amount));
             
            Console.WriteLine("\nOrder by Name then Amount:");
            accounts.OrderBy((x) => x.Name).ThenBy((x) => x.Amount).ForEach((x) => Console.WriteLine("{0}->{1}", x.Name, x.Amount));
            Console.WriteLine("\nCheck if John has an account:\n{0}",accounts.Any(x => x.Name.Equals("John")));
            Console.WriteLine("\nCheck if all accounts belong to John:\n{0}",accounts.All(x => x.Name.Equals("John")));
            Console.WriteLine("\nPopulate Range:");
            Enumerable.Range(1, 3).ForEach(Console.WriteLine);
            Console.WriteLine("\nDuplicate Number:");
            Enumerable.Repeat(1, 3).ForEach(Console.WriteLine);
            Console.WriteLine("\nJoin 2 entities:");
            accounts.Join(people, a => a.Name, p => p.Name, (a, p) => new { Name = p.Name, City = p.City, AccountName = a.AccountName, Amount = a.Amount }).ForEach((x) => Console.WriteLine("{0} from {1} -> {2} = {3}", x.Name, x.City, x.AccountName,x.Amount));            
        }
        [Test]
        public void TestFunction()
        {
            MyList<string> list = new MyList<string>();
            list.Add("One");
            list.Add("Two");
            list.Add("Three");
            Console.WriteLine("Print values that start with 'T':");
            list.PrintIf(x => x.StartsWith("T"),Console.WriteLine);
        }
    }
    
    class MyList<T>
    {
        List<T> data = new List<T>();
        public void Add(T obj)
        {
            data.Add(obj);
        }
        public void PrintIf(Func<T,bool> predicate, Action<T> action)
        {
            foreach (var d in data)
            {
                if (predicate(d))
                {
                    action(d);
                }
            }
        }
    }
}