Sign in to follow this  
Spa8nky

[C#] Sorting an array by int. How can I only sort a certain range?

Recommended Posts

Spa8nky    230
If I have an array of quadrangle objects that I wish to sort by their integer texture value I can do the following:
Array.Sort(quadrangles, delegate(Quadrangle quad1, Quadrangle quad2) { return quad1.Texture.CompareTo(quad2.Texture); });
How can I sort the same array (quadrangles) but specify the index range to sort? [Edited by - Spa8nky on April 28, 2010 9:27:37 AM]

Share this post


Link to post
Share on other sites
phresnel    953
By using the right overload:

int[] x = new int[] { 5, 4, 3, 2, 1 };
Array.Sort(x, 2, 3);
foreach (var y in x)
Console.WriteLine(y);


(there is also an overload to that overload which accepts a delegate)

Share this post


Link to post
Share on other sites
Spa8nky    230
Quote:

By using the right overload:


None of the overloads I have tried worked with my delegate, so I decided to post for help.

Quote:

(there is also an overload to that overload which accepts a delegate)



Array.Sort(quadrangles, 0, quadrangles_Index, delegate(Quadrangle quad1, Quadrangle quad2) { return quad1.Texture.CompareTo(quad2.Texture); });


This gives me the error of:

Cannot convert anonymous method to type 'System.Collections.Generic.IComparer<Cthonian.Quadrangle>' because it is not a delegate type :(

[Edited by - Spa8nky on April 28, 2010 10:48:50 AM]

Share this post


Link to post
Share on other sites
Spa8nky    230
I think I've solved the sorting problem now by using the following line:


Array.Sort(quadrangles, 0, quadrangles_Index, Quadrangle.SortTextureAscending());


and creating a class that inherits from IComparer:


private class SortTextureAscendingHelper : IComparer
{
int IComparer.Compare(object a, object b)
{
Quadrangle quad1 = (Quadrangle)a;
Quadrangle quad2 = (Quadrangle)b;

if (quad1.texture > quad2.texture)
{
return 1;
}
if (quad1.texture < quad2.texture)
{
return -1;
}
else
{
return 0;
}
}
}

public static IComparer SortTextureAscending()
{
return (IComparer)new SortTextureAscendingHelper();
}



Is there any way to avoid having to cast from an object each time?

Is this the simplest method of achieving an ascending sort by integer property?

Thank you.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Is there any way to avoid having to cast from an object each time?


You should be able to inherit from IComparer<Quadrangle> specifically, and then declare the parameters to Compare as Quadrangles.

Quote:
Is this the simplest method of achieving an ascending sort by integer property?


Assuming the texture values are numeric (i.e. can be subtracted instead of just compared), you should be able to implement the comparison as just 'quad1.texture - quad2.texture', since the interface doesn't care about the specific value returned, just whether it's negative/zero/positive.

Other than that, instead of implementing an IComparer and passing it, you could implement IComparable in Quadrangle, and skip the IComparer parameter.

Share this post


Link to post
Share on other sites
Mike.Popoloski    3258
I'd probably write an extension method:


static class ArrayExtensions
{
class FunctorComparer<T> : Comparer<T>
{
Comparison<T> comparison;

public FunctorComparer(Comparison<T> comparison)
{
this.comparison = comparison;
}

public override int Compare(T x, T y)
{
return comparison(x, y);
}
}

public static void Sort<T>(this T[] array, int startIndex, int count, Comparison<T> comparison)
{
Array.Sort(array, startIndex, count, new FunctorComparer<T>(comparison));
}
}

Share this post


Link to post
Share on other sites
Spa8nky    230
Thanks for the help guys. There is one last thing I'm not to clear on.

If I create a class member for the texture comparer as follows:


IComparer<Quadrangle> SortQuadrangleTextureAscending = new SortTextureAscendingHelper();


Then sort the array in a class method using that member:


Array.Sort(quadrangles, 0, quadrangles_Index, SortQuadrangleTextureAscending);


The comparer is only created once.

Is that preferable to creating a static method that returns the comparer:


class SortTextureAscendingHelper : IComparer<Quadrangle>
{
public int Compare(Quadrangle a, Quadrangle b)
{
// The interface doesn't care about the specific value returned, just whether it's negative/zero/positive
// Positive = a > b
// Negative = a < b
// 0 = a == b
return a.Texture - b.Texture;
}

public static IComparer<Quadrangle> SortTextureAscending()
{
return new SortTextureAscendingHelper();
}
}



then in the class method:


Array.Sort(quadrangles, 0, quadrangles_Index, SortTextureAscendingHelper.SortTextureAscending());


The reason why I ask is because I'm inclined to believe that a new instance of the comparer is returned each time it is called this way. Please let me know if this is wrong though.

Share this post


Link to post
Share on other sites
Zahlman    1682
If you have a non-static member then you're adding to the size of each object (although just a pointer, since your Comparer implementation is a class, i.e. a reference type). No reason it can't be static, though.

On the other hand, there's no reason you can't have a private static member and then have the public static method be an accessor for that member, rather than re-creating an instance.

On the other other hand, it doesn't really matter; either way, this isn't the "inner loop" - that's hidden inside the actual sort implementation. You create a comparer object at most once per sort call, which is much less work than the sort call will itself do.

(Also, I wouldn't put that much effort into documenting the optimization of using subtraction for this comparison; just make a note that it's optimized. The requirements on the return values, after all, are documented in the IComparer interface documentation. :) Besides, I was suggesting it more as an idiomatic simplification than as an optimization. ;) )

Share this post


Link to post
Share on other sites
Spa8nky    230
Thanks Zahlman,

Quote:
Original post by Zahlman
On the other hand, there's no reason you can't have a private static member and then have the public static method be an accessor for that member, rather than re-creating an instance.


So the following code will now only ever return one instance of the class, which is created only once, and not per call.


class SortTextureAscending : IComparer<Quadrangle>
{
static IComparer<Quadrangle> comparer = new SortTextureAscending();

public int Compare(Quadrangle a, Quadrangle b)
{
return a.Texture - b.Texture;
}

public static IComparer<Quadrangle> Comparer
{
get { return comparer; }
}
}

....
Array.Sort(quadrangles, 0, quadrangles_Index, SortTextureAscending.Comparer);



Have I understood you correctly?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this