4
public class foo {
  int ID { get; set; }
  byte[] sort { get; set; }
}

public class barMaster {
    public void FooSource() {
        return List<foo> FromDataSource;
    }
    public void display() {
        List<foo> sortedFoo = FooSource().OrderBy(f => f.sort);
        UIElement = sortedFoo;
    }

I have a set of objects that contain a byte[] property that I want to OrderBy, however, OrderBy(byte[]) throws an error:

System.ArgumentException: At least one object must implement IComparable.

What can I do to OrderBy byte[] values?

StormRider01
  • 335
  • 5
  • 19
  • you have to implement the interface ICompareble and create your own function for the order – Jorge May 18 '12 at 19:35
  • So if `f1` has `f1.sort == { 3, 201, 25, }` and `f2` has `f2.sort == { 3, 29, 7, 222, 0, }` which is greater, `f1` or `f2`, and why? – Jeppe Stig Nielsen May 18 '12 at 20:00
  • For my purpose, f1 would be considered greater than f2, as 201 > 29. I can see why this would be difficult behavior to bake into the framework from "Correct Behavior" perspective. – StormRider01 May 23 '12 at 19:42

4 Answers4

5

You can't order by the byte[] directly, since arrays don't implement IComparable. You would need to either order by the first byte (ie: OrderBy(f => f.sort[0]) or something else appropriate), or write your own IComparer<byte[]> and use that in the appropriate overload of OrderBy.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
5

As you've indicated that the arrays are of variable length (as it's a SQL Server hierarchy ID), you absolutely need to create a custom IComparer<byte[]> implementation.

The logic is simple:

  • Compare the first n bytes of each array byte-for-byte, where n is the number of bytes in the smaller of the two arrays. When a difference is detected between any byte, return the result of the comparison of the different bytes.
  • If the first n bytes are equal, return the comparison of the lengths of the two arrays.

This way, given a set of data like so:

00 01 02
00 01
01

When sorted, the results you'll get are:

00 01
00 01 02
01

That said, this is what your IComparer<byte[]> implementation will look like:

// I could be wrong in that this is called natural order.
class NaturalOrderByteArrayComparer : IComparer<byte[]>
{
    public int Compare(byte[] x, byte[] y)
    {
        // Shortcuts: If both are null, they are the same.
        if (x == null && y == null) return 0;

        // If one is null and the other isn't, then the
        // one that is null is "lesser".
        if (x == null && y != null) return -1;
        if (x != null && y == null) return 1;

        // Both arrays are non-null.  Find the shorter
        // of the two lengths.
        int bytesToCompare = Math.Min(x.Length, y.Length);

        // Compare the bytes.
        for (int index = 0; index < bytesToCompare; ++index)
        {
            // The x and y bytes.
            byte xByte = x[index];
            byte yByte = y[index];

            // Compare result.
            int compareResult = Comparer<byte>.Default.Compare(xByte, yByte);

            // If not the same, then return the result of the
            // comparison of the bytes, as they were the same
            // up until now.
            if (compareResult != 0) return compareResult;

            // They are the same, continue.
        }

        // The first n bytes are the same.  Compare lengths.
        // If the lengths are the same, the arrays
        // are the same.
        if (x.Length == y.Length) return 0;

        // Compare lengths.
        return x.Length < y.Length ? -1 : 1;
    }
}

As an aside, if your byte arrays were guaranteed to be the same length, as an alternative you can dynamically create the order by clause, sorting by the first element, then the second, etc, etc, like so:

static IEnumerable<foo> OrderBySortField(this IEnumerable<foo> items, 
    int sortLength)
{
    // Validate parameters.
    if (items == null) throw new ArgumentNullException("items");
    if (sortLength < 0) throw 
        new ArgumentOutOfRangeException("sortLength", sortLength,
            "The sortLength parameter must be a non-negative value.");

    // Shortcut, if sortLength is zero, return the sequence, as-is.
    if (sortLength == 0) return items;

    // The ordered enumerable.
    IOrderedEnumerable<foo> ordered = items.OrderBy(i => i.sort[0]);

    // Cycle from the second index on.
    for (int index = 1; index < sortLength; index++)
    {
        // Copy the index.
        int indexCopy = index;

        // Sort by the next item in the array.
        ordered = ordered.ThenBy(i => i.sort[indexCopy]);
    }

    // Return the ordered enumerable.
    return ordered;
}

And then you can simply call it like so:

// You have to supply the length of the array you're sorting on.
List<foo> sortedFoo = FooSource().
    OrderBySortField(sortLength).ToList();
Community
  • 1
  • 1
casperOne
  • 73,706
  • 19
  • 184
  • 253
  • The length of the array is not fixed, as it's a representation of a hierarchy from SQL server. – StormRider01 May 23 '12 at 19:49
  • @StormRider01 I've updated the answer to address your specific instance. I've left the solution from before, but the one where you're dealing with a variable length array is shown first. – casperOne May 23 '12 at 20:16
1

I know, it's an old question, but in a specific case, when the byte array contains a number (for example an IP address), the BitConverter class is available:

OrderBy(d => BitConverter.ToInt32(d.bytearray,0))

Source : https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/how-to-convert-a-byte-array-to-an-int

Joe Mayo
  • 7,501
  • 7
  • 41
  • 60
0

Unfortunately you cannot sort by a byte array as far as I can tell.

What you could do is have your foo class implement IComparable. Then in the overrideen compareTo method, write the comparision for the byte array as you like in your call. You can then replce the Order By with a simple sort:

FooSource().Sort();