views:

225

answers:

2

Hi,

I'm interested in sorting a collection, but also returning an index which can be used to map to the original position in the collection (before the sort).

Let me give an example to be more clear:

List<int> A = new List<int>(){3,2,1};
List<int> B;
List<int> idx;

Sort(A,out B,out idx);

After which:

A = [3,2,1] 
B = [1,2,3]
idx = [2,1,0]

So that the relationship between A,B,idx is:

A[i] == B[ idx[i] ] , for i = 0...2

Does C#/.Net have any built in mechanism to make this easy to implement?

Thanks.

+5  A: 
Mark Byers
totally forgot about linq :) very nice solution. thanks
homie347
+4  A: 

While Mark Byers provided you a solution using LINQ, I want to show you another solution using the .NET Framework.

There is an overload of Array.Sort that will do this for you:

int[] a = new[] { 3, 2, 1 };
int[] p = new[] { 0, 1, 2 };

Array.Sort(a, p);

Assert.IsTrue(a.SequenceEquals(new[] { 1, 2, 3 }));
Assert.IsTrue(p.SequenceEquals(new[] { 2, 1, 0 }));

Thus, here is a generic method meeting your specification that leverages this overload:

void Sort<T>(
    List<T> input,
    out List<T> output,
    out List<int> permutation,
    IComparer<T> comparer
) {
    if(input == null) { throw new ArgumentNullException("input"); }
    if(input.Count == 0) {
        // give back empty lists
        output = new List<T>(); 
        permutation = new List<int>();
        return;
    }
    if(comparer == null) { throw new ArgumentNullException("comparer"); }
    int[] items = Enumerable.Range(0, input.Count).ToArray();
    T[] keys = input.ToArray();
    Array.Sort(keys, items, comparer);
    output = keys.ToList();
    permutation = items.ToList();   
}
Jason
I would suggest using `Comparer<T>.Default` instead of throwing an `ArgumentNullExcpetion` if `comparer` is `null`. Otherwise, +1 for a complete, direct, and performant solution.
P Daddy
@P Daddy: Great suggestion, although I would handle that by providing an overload `Sort<T>(List<T>, out List<T>, out List<int>)` that calls the above method with `Comparer<T>.Default` as the `comparer`.
Jason