简体   繁体   中英

Linq OrderBy(Byte[]) values

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?

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 .

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();

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();   

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://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/how-to-convert-a-byte-array-to-an-int

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM