user7127000
user7127000

Reputation: 3233

Is there a way to use a custom OrderBy or Min without having to implement an IComparer<T>?

What I'm trying to write is

        return possibilities.OrderBy((x, y) =>
            {
                // returns true or false depending on whether x is 
                // lexographically smaller than y, e.g. x={1,2,3} 
                // is lexographically smaller than y={1,3,2}
                for(int i = 0; i < x.Length; ++i)
                {
                    if(x[i] < y[i])
                        return true;
                    else if(x[i] > y[i])
                        return false;
                }
                return true;
            })
            .First();

where possibilities is of type IEnumerable<int[]>. However, I was surprised that this syntax is not valid and that everything I can find by Googling indicates that I would have to write a bunch of extra code to implement an IComparer<int[]>. Really?

Upvotes: 2

Views: 240

Answers (3)

Slai
Slai

Reputation: 22876

Comparer<T>.Create(Comparison<T>) can be used in .NET 4.5 or later:

IEnumerable<int[]> a = new[] { new []{ 1, 2 }, new[] { 3, 4 } };

int[] min = a.OrderBy(x => x, Comparer<int[]>.Create((x, y) => {
    for (int z, i = 0; i < x.Length; i++)
        if ((z = x[i] - y[i]) != 0) return z;
    return 0;
})).FirstOrDefault();

But that's not needed to find the minimum:

int[] min = a.Aggregate((x, y) => {
    for (int i = 0; i < x.Length; ++i) {
        if (x[i] < y[i]) return x;
        if (x[i] > y[i]) return y;
    }
    return x;
});

Upvotes: 5

Eric Lizotte
Eric Lizotte

Reputation: 224

I think an easy way to do it would be

return possibilities.OrderBy(x => String.Join(".",x.ToString.PadLeft(10,'0'))).First();

the reason for the . is to separate the segments, the reason for the pad left to length of 10 with 0s is that the max int is 2 billion, so 10 maximum characters.

Upvotes: -2

Evk
Evk

Reputation: 101463

You can implement and reuse comparer which accepts compare delegate, like this:

public class DynamicComparer<T> : IComparer<T> {
    private readonly Func<T, T, int> _comparer;
    public DynamicComparer(Func<T, T, int> comparer) {
        _comparer = comparer;
    }
    public int Compare(T x, T y) {
        return _comparer(x, y);
    }
}

And then use it in all places you need:

possibilities.OrderBy(c => c, new DynamicComparer<int[]>((x, y) =>
{
    for (int i = 0; i < x.Length; ++i)
    {
        if (x[i] < y[i])
            return -1;
        else if (x[i] > y[i])
            return 1;
    }
    return 0;
}));

Note that as another answer suggests, there is already such comparer in .NET 4.5, which can be created with Comparer<int[]>.Create. If you are on .NET 4.5 - no need to create your own comparer like that.

You can even go futher and implement your own OrderBy extension method:

public static class Extensions {
    public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> items, Func<T, T, int> comparer) {
        return items.OrderBy(c => c, new DynamicComparer<T>(comparer));
    }
}

Then it becomes just like you want:

possibilities.OrderBy((x, y) =>
{
    for (int i = 0; i < x.Length; ++i)
    {
      if (x[i] < y[i])
          return -1;
      else if (x[i] > y[i])
          return 1;
      }
      return 0;
 });

Upvotes: 1

Related Questions