linq order by a 属性 这是一个数组?

linq order by a property which is an array?

有这个代码

static void Main(string[] args)
{
    var test = new List<Tuple<string, int[]>>();

    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 4, 7, 8 })); //item 1
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 2, 6, 5 })); //item 2
    test.Add(new Tuple<string, int[]>("b", new int[] { 1, 4, 7, 7 })); //item 3
    test.Add(new Tuple<string, int[]>("b", new int[] { 1, 2, 3, 4 })); //item 4
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 1, 4, 9 })); //item 5

    var result = test.OrderBy(x => x.Item1).ThenBy(x => x.Item2.OrderBy(y => y));
}

return At least one object must implement IComparable.

不太确定如何编写 linq 所以我会得到这个结果

  1. 项目 5
  2. 项目 2
  3. 项目 1
  4. 项目 4
  5. 项目 3

这似乎是我想要的

var result = test.OrderBy(x => x.Item1)
                 .ThenBy(x => string.Join(",", x.Item2.OrderBy(y => y)));

并且仅适用于相同数量的数字...

我是在胡乱猜测,因为您没有指定要求(为什么一个数组会出现在另一个数组之前),但考虑到评论,我会提供一个 "should" 有效的答案(不在我的脑海中)并没有真正优化):

class ArrayComparer : IComparer<int[]>
{
  public int Compare(int[] x, int[] y)
  {
    for (var i = 0; i < x.Length && i < y.Length; i++)
    {
      if (x[i] > y[i])
        return 1;
      if (y[i] > x[i])
        return -1;
    }
    if (y.Length > x.Length)
      return -1;
    else if (y.Length < x.Length)
      return 1;
    return 0;
  }
}

然后:

var result = test.OrderBy(x => x.Item1)
        .ThenBy(x => x.Item2.OrderBy(y => y).ToArray(), new ArrayComparer());

我实际测试了一下,结果是这样的:

a -> 1,1,4,9 // item 5
a -> 1,2,6,5 // item 2
a -> 1,4,7,8 // item 1
b -> 1,2,3,4 // item 4
b -> 1,4,7,7 // item 3

自定义 IComparer<int[]> 就是您想要的。这是我的写法。这将查看各个值,直到发现差异为止。如果在到达任一数组的末尾之前没有发现差异,那么它 returns 基于数组的长度,其度量是较短的数组更小。

public class ArrayComparer : IComparer<int[]>
{
    public int Compare(int[] x, int[] y)
    {
        for(int i = 0; i < x.Length && i < y.Length; i++)
        {
            if (x[i] != y[i])
                return x[i] - y[i];
        }

        return x.Length - y.Length;
    }
}

使用减法进行比较非常适合 IComparer 的 return 值的指定方式。

要使用它,您只需编写

var result = test.OrderBy(x => x.Item1).ThenBy(x => x.Item2.OrderBy(y => y).ToArray(), new ArrayComparer());

对于我的回答,我创建了一个自定义比较来比较 Tuple 类型和 SequenceCompare 以一般地比较任何类型的数组。我相信这会很快。

这也可以很好地处理不同大小的数组——较长的数组是 "larger"。

static void Main(string[] args)
{
    var test = new List<Tuple<string, int[]>>();

    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 4, 7, 8 })); //item 1
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 2, 6, 5 })); //item 2
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 2, 6  })); //item 2
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 2,  5 })); //item 2
    test.Add(new Tuple<string, int[]>("a", new int[] { 1,  6, 5 })); //item 2
    test.Add(new Tuple<string, int[]>("a", new int[] {  2, 6, 5 })); //item 2
    test.Add(new Tuple<string, int[]>("b", new int[] { 1, 2, 3, 4 })); //item 4
    test.Add(new Tuple<string, int[]>("a", new int[] { 1, 1, 4, 9 })); //item 5

    var result = test.OrderBy(x => x,new CustomComp()) ;

    result.Dump();
}


public class CustomComp : IComparer<Tuple<string, int[]>>
{
   public int Compare(Tuple<string, int[]> x,Tuple<string, int[]> y)
   {
     int strR = string.Compare(x.Item1,y.Item1);
     if (strR == 0)
       return x.Item2.SequenceCompare(y.Item2);
     else
     return strR;
   }
}

static class Compare
{
  public static int SequenceCompare<TSource>(this IEnumerable<TSource> x, IEnumerable<TSource> y)
  {
    return SequenceCompare(x,y,System.Collections.Generic.Comparer<TSource>.Default);
  }

  public static int SequenceCompare<TSource>(this IEnumerable<TSource> x, IEnumerable<TSource> y, IComparer<TSource> comparer)
  {
    if (x == null) throw new ArgumentNullException("x");
    if (y == null) throw new ArgumentNullException("y");

    using (IEnumerator<TSource> xe = x.GetEnumerator(), ye = y.GetEnumerator())
    {
      while (true)
      {
        bool next1 = xe.MoveNext();
        bool next2 = ye.MoveNext();

        // Are not of same length. longer one >
        if (next1 != next2)
        {
            if (next1 == true) return 1;
            return -1;
        }

        // Both finished -- equal
        if (!next1) return 0;

        int result = comparer.Compare(xe.Current, ye.Current);

        if (result != 0) return result;
      } 
    }
  }
}