有点复杂的列表排序

时间:2014-07-25 05:33:32

标签: c# list sorting

我有许多对象,每个对象都有3个数字属性:“高”,“低”和“决胜局”。它们将按如下方式排序:如果对象的低值高于另一个对象的高值,则它在列表中显示在它之前。同样,如果一个对象的高位低于另一个低位,它会出现在列表的后面。但是在两个对象具有冲突范围的情况下(例如,一个高在另一个对象的低和高之间),会考虑仲裁器属性,其中具有较高仲裁器值的对象会在列表的前面放置。

我特意使用c#,但我认为这里的想法与语言无关,因此任何类型的代码(没有双关语)都是受欢迎的。

另外,我已经自己做了这件事。我有一个嵌套的for循环,到目前为止对我来说还没有用。我会放弃一些代码,但我在手机上,这使它成为一件苦差事。此外,这对你来说可能很有趣,无论如何你都不需要我的丑陋代码。

5 个答案:

答案 0 :(得分:3)

你是否假设Min <= Tie <= Max?您在问题中没有这样说,如果不这样做,排序顺序没有明确定义,因为它不是transitive。例如,将范围写为[Min, Tie, Max],请考虑:

A: [5,-10,  6]
B: [0,  1, 10]
C: [2,  3,  4]

A&lt; B(因为它们重叠并且-10 <1)

B&lt; C(因为它们重叠并且1 <3)

但是A&gt; C(因为它们不重叠且5> 4)

如果是,您可以为Range课程定义自定义IComparer<Range>,并将其传递给任何c#sort method

更新,这是一个这样的实现。

public struct RangeWithTie<T> where T : IEquatable<T>, IComparable<T>
{
    readonly T min;
    readonly T max;
    readonly T tie;
    readonly bool isNonEmpty;

    public static Range<T> Empty = new Range<T>();

    public static IComparer<RangeWithTie<T>> CreateSortingComparer()
    {
        return new RangeWithTieComparer();
    }

    public RangeWithTie(T start, T tie, T end)
    {
        // Enfore start <= tie <= end
        var comparer = Comparer<T>.Default;
        if (comparer.Compare(start, end) > 0) // if start > end
        {
            throw new ArgumentOutOfRangeException("start and end are reversed");
        }
        else if (comparer.Compare(start, tie) > 0)
        {
            throw new ArgumentOutOfRangeException("tie is less than start");
        }
        else if (comparer.Compare(tie, end) > 0)
        {
            throw new ArgumentOutOfRangeException("tie is bigger than end");
        }
        else
        {
            this.min = start;
            this.max = end;
            this.tie = tie;
        }
        this.isNonEmpty = true;
    }

    public T Min { get { return min; } }

    public T Max { get { return max; } }

    public T Tie { get { return tie; } }

    public bool IsEmpty { get { return !isNonEmpty; } }

    public class RangeWithTieComparer : IComparer<RangeWithTie<T>>
    {
        #region IComparer<RangeWithTie<T>> Members

        public int Compare(RangeWithTie<T> x, RangeWithTie<T> y)
        {
            // return x - y.
            if (x.IsEmpty)
            {
                if (y.IsEmpty)
                    return 0;
                else
                    return -1;
            }
            else if (y.IsEmpty)
            {
                return 1;
            }
            var comparer = Comparer<T>.Default;
            if (comparer.Compare(y.Min, x.Max) > 0)
                return -1;
            else if (comparer.Compare(x.Min, y.Max) > 0)
                return 1;
            return comparer.Compare(x.Tie, y.Tie);
        }

        #endregion
    }

    public override string ToString()
    {
        if (IsEmpty)
            return "Empty";
        StringBuilder s = new StringBuilder();
        s.Append('[');
        if (Min != null)
        {
            s.Append(Min.ToString());
        }
        s.Append(", ");
        if (Tie != null)
        {
            s.Append(Tie.ToString());
        }
        s.Append(", ");
        if (Max != null)
        {
            s.Append(Max.ToString());
        }
        s.Append(']');
        return s.ToString();
    }
}

这可以这样使用:

var sortedRanges = ranges.OrderBy(x => x, RangeWithTie<double>.CreateSortingComparer()).ToArray();

我没有直接生成结构实现IComparer<RangeWithTie<T>>,因为具有相同比较的范围不一定相等。例如,[-1,0,1][-2,0,1]具有相同的比较但不相等。

答案 1 :(得分:1)

快速解决方案和用于测试它的控制台应用程序。此方法将返回两个对象中较大的一个。只需将dynamic替换为您需要的适当对象类型。

class Program
{
    private static object Sort(dynamic first, dynamic second)
    {
        if (OverlapExists(first, second))
        {
            // Note: If tiebreakers are equal, the first will be returned:
            return first.tiebreaker >= second.tiebreaker ? first : second;
        }
        else
        {
            // Note: Only need to test one value (just high); Since we know 
            // there is no overlap, the whole object (both high and low) must 
            // be either over  or under that which it is compared to:
            return first.high > second.high ? first : second;
        }
    }

    private static bool OverlapExists(dynamic first, dynamic second)
    {
        return (first.low < second.high) && (second.low < first.high);
    }

    static void Main(string[] args)
    {

        dynamic first = new {name="first", high = 10, 
                             tiebreaker = 5, low = 1 };
        dynamic second = new {name="second", high = 15, 
                              tiebreaker = 12, low = 11 };
        dynamic third = new {name="third", high = 20, 
                             tiebreaker = 9, low = 6 };

        var firstResult = Sort(first, second);
        var secondResult = Sort(first, third);
        var thirdResult = Sort(second, third);

        Console.WriteLine("1) " + first.ToString() 
                             + "\nVS: " + second.ToString());
        Console.WriteLine("Winner: " + firstResult.name);

        Console.WriteLine("\n2) " + first.ToString() 
                             + "\nVS: " + third.ToString());
        Console.WriteLine("Winner: " + secondResult.name);

        Console.WriteLine("\n3) " + second.ToString() 
                             + "\nVS: " + third.ToString());
        Console.WriteLine("Winner: " + thirdResult.name);

        Console.ReadKey();
    }
}

答案 2 :(得分:0)

假设您有一个List<T>(T为您的objects,具有高,低和领带属性),那么您可以使用

list.Sort(…);

Comparison<T>作为参数。这是一个委托你需要2个对象,并且应该返回&lt; 0,当你的对象的第一个实例应该是另一个实例的头部时,如果它们具有相同的顺序,则为0;如果第二个第二个对象应该在第一个实例之前,则为> 0; 或者您可以传递一个自定义比较器(实现IComparer<T>),它与Comparison<T>基本相同但是通知接口。

答案 3 :(得分:0)

无论您的逻辑是什么,您都可以实现IComparable以启用Array或List的排序功能。因此,如下面的代码所示,

public class MyStuff : IComparable<MyStuff>
{
    public int High { get; set; }
    public int Low { get; set; }
    public int TieBreaker { get; set; }

    public int CompareTo(MyStuff other)
    {
        // if an object's low is higher than another object's high, 
        // it appears before it in the list
        if ((this.Low > other.High) ||
            // if its high is between the other object's low and 
            // high then compare their tiebreaker
            (this.High > other.Low && this.High < other.High && 
                this.TieBreaker > other.TieBreaker))
            return 1;
        else if (this.Low == other.High)
            return 0;
        else
            return -1;
    }
}

基本思路是CompareTo返回1(在其他之前移动),0(保留两个位置)或-1(在其他位置之后移动),具体取决于您的排序逻辑。

答案 4 :(得分:-1)

请参阅IComparable<T>

class DataObject : IComparable<DataObject>
{
    public double High, Low, Tiebreaker;

    public int CompareTo(DataObject obj) 
    {
        // this doesn't seem to make sense as a range sort, but seems to match your question...
        // low > another high
        if (this.Low != obj.High)
            return this.Low.CompareTo(obj.High);
        // otherwise sort tiebreaker ascending
        else this.TieBreaker.CompareTo(obj.TieBreaker);
    }
}

用作

var items = new[] { new DataObject(1,2,3), new DataObject(4,5,6) };
Array.Sort<DataObject>(items);

// items is now sorted