简体   繁体   English

实现IComparer结合多个Linq OrderBy

[英]Implementing IComparer combining multiple Linq OrderBy's

My problem is that I always want to order a collection of objects in a certain fashion. 我的问题是我总是想以某种方式订购一组对象。

For example: 例如:

class foo{
public string name {get;set;}
public DateTime date {get;set;}
public int counter {get;set;}
}

... ...

IEnumerable<foo> dosomething(foo[] bar){ 
return bar.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
}

The issue I have is its quite longwinded tacking-on the sort order all the time. 我遇到的问题是它总是很长时间地处理排序顺序。 A neat solution appears to just create a class that implements IComparer<foo> , meaning I can do: 一个简洁的解决方案似乎只是创建一个实现IComparer<foo> ,这意味着我可以这样做:

IEnumerable<foo> dosomething(foo[] bar){ 
return bar.OrderBy(a=>a, new fooIComparer())
}

.

The problem is, the order method this implements is as follows 问题是,这个实现的顺序方法如下

... ...

public int Compare(foo x, foo y){ }

Meaning it compares on a very granular basis. 意思是它在非常精细的基础上进行比较。

The currently implementation (which will probably work, although im writing pseudocode) 当前的实现(虽然我正在编写伪代码,但可能会有效)

public int Compare(foo x, foo y){
if (x==y)
  return 0;
var order = new []{x,y}.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
  return (order[0] == x) ? -1 : -1;//if x is first in array it is less than y, else it is greater
}

This is not exactly efficient, can another offer a neater solution? 这不是很有效,另一个可以提供更整洁的解决方案吗? Ideally without a Compare(x,y) method altogether? 理想情况下,没有比较(x,y)方法?

Option 1 - The Comparer 选项1 - 比较器

As you're ordering by multiple conditions, you'll to check them individually within each case; 当您按多种条件订购时,您将在每种情况下单独检查; for example, if x.name and y.name are equal, then you would check x.date and y.date , and so on. 例如,如果x.namey.name相等,那么您将检查x.datey.date ,依此类推。

public class FooComparer : IComparer<Foo>
{
    public int Compare(Foo x, Foo y)
    {
       // nasty null checks!
        if (x == null || y == null)
        {
            return x == y ? 0
                : x == null ? -1
                : 1;
        }

        // if the names are different, compare by name
        if (!string.Equals(x.Name, y.Name))
        {
            return string.Compare(x.Name, y.Name);
        }

        // if the dates are different, compare by date
        if (!DateTime.Equals(x.Date, y.Date))
        {
            return DateTime.Compare(x.Date, y.Date);
        }

        // finally compare by the counter
        return x.Counter.CompareTo(y.Counter);
    }
}

Option 2 - The extension method 选项2 - 扩展方法

An alternative, not so appealing approach, could be an extension method. 另一种不那么吸引人的方法可能是一种扩展方法。 Sadly as the TKey for each ThenBy can be different, we lose the power of generics, but can safely replace it with the type object in this case. 遗憾的是,由于每个ThenByTKey可能不同,我们失去了泛型的力量,但在这种情况下可以安全地用类型object替换它。

public static IOrderedEnumerable<T> OrderByThen<T>(this IEnumerable<T> source, Func<T, object> selector, params Func<T, object>[] thenBySelectors)
{
    IOrderedEnumerable<T> ordered = source.OrderBy(selector);
    foreach (Func<T, object> thenBy in thenBySelectors)
    {
        ordered = ordered.ThenBy(thenBy);
    }

    return ordered;
}

You have to implement IComparable<foo> and compare all properties: 您必须实现IComparable<foo>并比较所有属性:

class foo: IComparable<foo>, IComparer<foo>
{
    public string name { get; set; }
    public DateTime date { get; set; }
    public int counter { get; set; }

    public int Compare(foo x, foo y)
    {
        if (x == null || y == null) return int.MinValue;
        if (x.name != y.name)
            return StringComparer.CurrentCulture.Compare(x.name, y.name);
        else if (x.date != y.date)
            return x.date.CompareTo(y.date);
        else if (x.counter != y.counter)
            return x.counter.CompareTo(y.counter);
        else
            return 0;
    }

    public int CompareTo(foo other)
    {
        return Compare(this, other);
    }
}

Then you can use OrderBy in this way: 然后你可以用这种方式使用OrderBy

var ordered = foos.OrderBy(f => f).ToList();

扩展方法有什么问题?

Why wont you simply compare your values: 为什么不简单地比较你的价值观:

int Compare(foo x, foo y)
{

    if (x== null && y == null)
        return 0;
    else if (x == null)
        return -1;
    else if (y == null)
        return 1;

    var nameComparision = string.Compare(x.name,y.name);
    if (nameComparision != 0)
        return nameComparision;
    var dateComparision = x.date.CompareTo(y.date);
    if (dateComparision != 0)
        return dateComparision;
    var counterComparision  = x.counter.CompareTo(y.counter);
    return counterComparision;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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