แสดงบทความที่มีป้ายกำกับ enumerable แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ enumerable แสดงบทความทั้งหมด

วันพฤหัสบดีที่ 15 พฤษภาคม พ.ศ. 2551

Extension Methods for Enumerable Part II - Index injection and index extraction

In enumerable type, index is useful to identify a member. In C# 3.0, some methods for enumerable provide index parameter such as Select and Where. But most of methods are still not provide index, such as GroupBy and OrderBy.

One of the easy way to inject index into enumerable is to use Closure. For example, if you want to order "ABCDEFG" alternately you may write.

int i = 0;
var e = "ABCDEFG".OrderBy(c => i++ % 2);
Console.WriteLine(e.ListMembers());
//A C E G B D F


To inject an index, you may declare a variable (i) and use this variable as an index in the delegate. You must also put incremental operator (++) to index variable to make index increase every iteration.

Anyway, using of Closure also has drawback. The index variable will not be cleared after enumeration. When you enumerate again index variable will be incorrect. For example:

int i = 0;
var e = "ABCDEFG".OrderBy(c => i++ % 2);
Console.WriteLine(e.ListMembers());
//A C E G B D F
Console.WriteLine(e.ListMembers());
//B D F A C E G


As you can see, the third command and the forth command are the same. But the result is not the same. Because, after run the third command, i value is equal to 7. Then when you call the forth command, the index will be started at index 7 and so on. Finally, the result will not be the same for the second time.

Anyway, this issue can be solved by make the class persistent.

int i = 0;
var e = "ABCDEFG".OrderBy(c => i++ % 2).ToArray();
Console.WriteLine(e.ListMembers());
//A C E G B D F
Console.WriteLine(e.ListMembers());
//A C E G B D F


Above example, we call ToArray after OrderBy method, and make e variable persistent. The result for e will be the same everytime.

Another solution for index injection is to create extension methods.

public static Dictionary<int, T> WithIndex<T>(this IEnumerable<T> source) {
    return source.WithIndex(0.To(int.MaxValue), EqualityComparer<int>.Default);
}
public static Dictionary<T1, T2> WithIndex<T1, T2>(this IEnumerable<T2> source, IEnumerable<T1> index) {
    return source.WithIndex(index, EqualityComparer<T1>.Default);
}
public static Dictionary<T1, T2> WithIndex<T1, T2>(this IEnumerable<T2> source, IEnumerable<T1> index, IEqualityComparer<T1> comparer) {
    Dictionary<T1, T2> dict = new Dictionary<T1, T2>(comparer);
    using (IEnumerator<T2> source_enum = source.GetEnumerator()) {
        using (IEnumerator<T1> index_enum = index.GetEnumerator()) {
            while (source_enum.MoveNext()) {
                if (!index_enum.MoveNext())
                    throw new ArgumentException("index cannot has less length than source", "index");
                dict.Add(index_enum.Current, source_enum.Current);
            }
        }
    }
    return dict;
}


You just call above method before enumeration. The result you get is a dictionary. Key is index, and Value is object. Then after enumeration, you may call Select method to remove index from the object. For example:

var e = from c in "ABCDEFG".WithIndex()
        orderby c.Key % 2
        select c.Value;
Console.WriteLine(e.ListMembers());
//A C E G B D F
Console.WriteLine(e.ListMembers());
//A C E G B D F


You can also pass any kind of index into WithIndex method. The index's length must be greater than or equal to source's length.

For example, you have source and position. And you want to sort source by position. In this case, you can assign position as an index of the source.

var e = from x in source.WithIndex(position)
        orderby x.Key
        select x.Value;


Index extraction is index from specific item in the enumerable. Here is the extension methods:

public static int IndexOf<T>(this IEnumerable<T> source, T item) {
    return source.IndexOf(item, EqualityComparer<T>.Default);
}
public static int IndexOf<T>(this IEnumerable<T> source, T item, IEqualityComparer<T> comparer) {
    IList<T> list = source as IList<T>;
    if (list != null)
        return list.IndexOf(item);

    int i = 0;
    foreach (T x in source) {
        if (comparer.Equals(x, item))
            return i;
    }
    return -1;
}


The example for IndexOf is from CodeGolf. The question is Reverse game. You will get list of numbers randomly. And you must tell how many numbers from the left to reverse. And target is to arrage the numbers.

int[] list = new[] { 8, 9, 4, 5, 1, 3, 6, 2, 7 };

Action<int> reverse = i => {
    if (i > 1) {
        Console.WriteLine("reverse: {0}", i);
        list = list.Take(i).Reverse().Concat(list.Skip(i)).ToArray();
        Console.WriteLine(list.ListMembers());
    }
};

list.Length.DownTo(1).ForEach(x => {
    if (x != list[x - 1]) {
        reverse(list.IndexOf(x) + 1);
        reverse(x);
    }
});
//8 9 4 5 1 3 6 2 7
//reverse: 2
//9 8 4 5 1 3 6 2 7
//reverse: 9
//7 2 6 3 1 5 4 8 9
//reverse: 7
//4 5 1 3 6 2 7 8 9
//reverse: 5
//6 3 1 5 4 2 7 8 9
//reverse: 6
//2 4 5 1 3 6 7 8 9
//reverse: 3
//5 4 2 1 3 6 7 8 9
//reverse: 5
//3 1 2 4 5 6 7 8 9
//reverse: 3
//2 1 3 4 5 6 7 8 9
//reverse: 2
//1 2 3 4 5 6 7 8 9


Above example, you need IndexOf to identify position of the next number you want to reverse in the list. Then you reverse that number to the left, and then you reverse again to move the number to appropriate position.

Next, we will go to Roman Numerals.

วันศุกร์ที่ 18 มกราคม พ.ศ. 2551

Extension Methods for Enumerable

Visual Studio 2008 provides many extension method for enumerable, extension method in this post is additional extension that I wrote while developing programs.

public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source, int num) {
    for (int x = 0; x < num; x++) {
        using (IEnumerator<T> source_enum = source.GetEnumerator()) {
            while (source_enum.MoveNext())
                yield return source_enum.Current;
        }
    }
}
public static IEnumerable<T> AsEnumerable<T>(T item) {
    yield return item;
}


Repeat method is to reiterate enumerable for specific amount of times. This method is unlike built-in Repeat method of VS 2008. The built-in Repeat method repeats a single item, but this Repeat method repeats an enumerable.

var result = new[] { 1, 2, 3 }.Repeat(3);
//result = {1, 2, 3, 1, 2, 3, 1, 2, 3}


Example above makes the repeat of array {1, 2, 3} for 3 times.

AsEnumerable method makes single item to an enumerable of single element. This method is unlike built-in AsEnumerable method of VS 2008. The built-in AsEnumerable method just cast an enumerable to IEnumerable interface. This AsEnumerable method is not casting, but wrap single item with enumerable.

var result = new[] { 1, 2, 3 }.Concat(Enumerable2.AsEnumerable(4));
//result = {1, 2, 3, 4}


Example above array of {1, 2, 3} is able to concatenate with enumerable of 4.

Some more functions:

public static bool All<T>(this IEnumerable<T> source, Func<T, int, bool> predicate) {
    int i = 0;

    foreach (T item in source) {
        if (!predicate(item, i++))
            return false;
    }
    return true;
}
public static bool Any<T>(this IEnumerable<T> source, Func<T, int, bool> predicate) {
    int i = 0;

    foreach (T item in source) {
        if (predicate(item, i++))
            return true;
    }
    return false;
}


Both All and Any methods are the same with built-in All/Any methods, but the different is these methods provide index used in predicate function.

var result = new[] { 0, 1, 2 }.All((x, i) => x == i);
//result = true

var result2 = new[] { 2, 1, 0 }.Any((x, i) => x == i);
//result2 = true


Example above, All method tests every element's value is equal to index, which is true. Any method tests any element's value is equal to index, it is true because number 1's value is equal to index.

Last 2 functions, these functions are not extension of enumerable, but they are related to enumerable.

public static IEnumerable<T> Enum<T>() {
    return System.Enum.GetValues(typeof(T)).Cast<T>();
}
public static T[] Append<T>(this T[] source, T item) {
    int len = source.Length;
    T[] ret = new T[len + 1];
    source.CopyTo(ret, 0);
    ret[len] = item;
    return ret;
}


Enum method creates enumerable from enum.

var result = Enumerable2.Enum<DayOfWeek>();
//result = {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}


Example above, Enum method creates enumerable of DayOfWeek type. Values range from Sunday to Saturday.

Append method appends an item to an array.

var result = new[] { 1, 2, 3 }.Append(4);
//result = {1, 2, 3, 4}


Example above 4 is appended to array of {1, 2, 3} and results to new array of {1, 2, 3, 4}.

Next post is still be on more Extension Methods.

วันอาทิตย์ที่ 16 ธันวาคม พ.ศ. 2550

How to use Extension Methods to extend C# language

Start my first blog with coding!

Usually, when we want to extend a type, we need to create a new derived type. But creating a new derived type, sometimes it is not suitable. Sometimes the extensions are so small, sometimes types are sealed and unable to be extended. Another solution is you can create new static methods. But those methods are not become parts of the type. New Visual Studio 2008 has a new feature enables you to add new methods without having to derive a type and able to create new methods that will become a part of the type. This new feature called "Extension Methods".

Interesting point is, we can use "Extension Methods" to enhance built-in types like int or IEnumerable. And that could change style of coding which totally different from C# 2.0. Do you know Ruby? Ruby is a programming language that is short and easy. Let's make C# to be as easy as Ruby.

Start with foreach. When you enumerate items in a list you use:

foreach (Object item in list)

But not in Ruby, in Ruby we just write:

list.each{|item| ...}

How easy! To make code like Ruby, put this source on your extension class.

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) {
    foreach (T item in source)
        action(item);
}

public static void ForEach<T>(this IEnumerable<T> source, Action<T, int> action) {
    int i = 0;

    foreach (T item in source)
        action(item, i++);
}


After extension, you can write:

list.ForEach(item => ...);
list.ForEach((item,index)=> ...);

Here is the example:

string[] persons = new string[] {"Jack", "Lisa", "Ryan"};
persons.ForEach(person => Console.WriteLine("Hello {0}!", person));
//Hello Jack!
//Hello Lisa!
//Hello Ryan!


persons.ForEach((person, index) => Console.WriteLine("{0}. {1}", index + 1, person));
//1. Jack
//2. Lisa
//3. Ryan


Is that ok with you? If you like Ruby style go to next method. When we run number 0 to 2 (run 3 loop) we usaully write:

for (int i = 0; i <= 2; i++)

Ruby is much easily:

0..2

or

3.times{|x| ..}

Jealous? Put this source on your extension class:

public static IEnumerable<int> To(this int from, int to) {
    for (int x = from; x <= to; x++)
        yield return x;
}
public static IEnumerable<int> DownTo(this int from, int to) {
    for (int x = from; x >= to; x--)
        yield return x;
}
public static IEnumerable<int> StepTo(this int from, int to, int step) {
    if (step > 0) {
        for (int x = from; x <= to; x += step)
            yield return x;
    } else if (step < 0) {
        for (int x = from; x >= to; x += step)
            yield return x;
    } else {
        throw new ArgumentException("Step cannot be zero.", "step");
    }
}
public static void Times(this int num, Action<int> action) {
    for (int x = 0; x < num; x++)
        action(x);
}


Now you can write:

string[] persons = new string[] { "Jack", "Lisa", "Ryan" };
0.To(2).ForEach(i => Console.WriteLine(persons[i]));
//Jack
//Lisa
//Ryan

2.DownTo(0).ForEach(i => Console.WriteLine(persons[i]));
//Ryan
//Lisa
//Jack

0.StepTo(2, 2).ForEach(i => Console.WriteLine(persons[i]));
//Ryan
//Jack
//(run 0 to 2 with step 2)

3.Times(i => Console.WriteLine(persons[i]));
//Jack
//Lisa
//Ryan


Try practical use, with question from projecteuler.net.
Add all the natural numbers below 1000 that are multiples of 3 or 5.

Console.WriteLine(1.To(999).Where(x => (x % 3 == 0) || (x % 5 == 0)).Sum());
//233168


Now we can completed question above with only one line of code. If you use C# 2.0, you will never write code like this. There are more several methods that you can extend C# from Ruby (or any other languages). Make your code shorter, your life will be easier.