如果有多个属性,如何对列表类型的泛型进行排序?

6

我有一个泛型列表,其中包含一个属性(类类型)。我需要一个针对Z参数(TrainingSet)的排序方法:

public override List<TrainingSet> CalculatedDistancesArray
    (List<TrainigSet> ts, double x, double y, int k)
{
    for (int i =0; i < ts.Count; i++)
    {
        ts[i].Z = (Math.Sqrt(Math.Pow((ts[i].X - x), 2) 
                  + Math.Pow((ts[i].Y - y), 2)));
    }
    // I want to sort according to Z
    ts.Sort(); //Failed to compare two elements in the array.
    List<TrainingSet> sortedlist = new List<TrainingSet>();
    for (int i = 0; i < k; i++)
    {
        sortedlist.Add(ts[i]);
    }
    return ts;
}

public class TrainigSet
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }
    public string Risk { get; set; }
}

顺便提一下,复制列表还有更简单的方法,而且你真的想先对“输入”列表进行排序,然后再将其复制到新列表中吗? - Jon Skeet
他并不是在拷贝整个列表,而只是前k项。 - Guffa
5个回答

20

仅按单个属性排序很容易。使用接受 Comparison<T> 的重载方法:

// C# 2
ts.Sort(delegate (TrainingSet o1, TrainingSet o2) 
       { return o1.Z.CompareTo(o2.Z)); }
);

// C# 3
ts.Sort((o1, o2) => o1.Z.CompareTo(o2.Z));

在多个属性上进行排序有点棘手。我创建了类来以复合方式构建比较,以及构建“投影比较”,但如果你真的只想按Z排序,则上面的代码将是最简单的。

如果你正在使用.NET 3.5,并且你不需要对列表进行原地排序,你可以使用OrderBy和ThenBy,例如:

return ts.OrderBy(t => t.Z);

或者更复杂的比较:

return ts.OrderBy(t => t.Z).ThenBy(t => t.X);

这些将在查询表达式中用orderby子句表示:
return from t in ts
       orderby t.Z
       select t;

并且

return from t in ts
       orderby t.Z, t.X
       select t;

(如果您想的话,也可以按降序排序。)

3
var sortedList = 
      list.OrderBy(i => i.X).ThenBy(i => i.Y).ThenBy(i => i.Z).ToList();

注意,这并不会就地对列表进行排序,这是原始帖子可能想要的 - 这并不清楚。 - Jon Skeet
@ykaratoprak:看看Jon Skeet的回答,他有一个2.0版本。 - Mehrdad Afshari

1
你可以尝试这个。它对我起了作用:

ts.Sort(delegate(TrainingSet a, TrainingSet b) { return a.X.CompareTo(b.X) != 0 ? a.X.CompareTo(b.X) : a.Y.CompareTo(b.Y); });

0

使用框架3.5,这将是非常简单的:

public override List<TrainingSet> CalculatedDistancesArray(List<TrainigSet> ts, double x, double y, int k) {
   foreach (TrainigSet t in ts) {
      t.Z = Math.Sqrt(Math.Pow(t.X - x, 2) + Math.Pow(t.Y - y, 2));
   }
   return ts.OrderBy(t => t.Z).Take(k).ToList();
}

注意:这不会改变ts列表的顺序,而是创建一个新的排序列表进行返回。
(我假设您实际上想要返回列表中的前k个项目,而不是在问题代码中所做的那样返回ts列表。)
使用框架2需要更多的代码:
public override List<TrainingSet> CalculatedDistancesArray(List<TrainigSet> ts, double x, double y, int k) {
   foreach (TrainigSet t in ts) {
      t.Z = Math.Sqrt(Math.Pow(t.X - x, 2) + Math.Pow(t.Y - y, 2));
   }
   ts.Sort(delegate (TrainigSet t1, TrainigSet t2) { return t1.Z.CompareTo(t2.Z)); });
   List<TrainigSet> result = new List<TrainigSet>(k);
   for (int i = 0; i < k ; i++) {
      result.Add(ts[i]);
   }
   return result;
}

如果您仅使用Z值进行排序,可以跳过Math.Sqrt调用,并将值保留为距离的平方,因为这与距离完全相同。


0
如果您在类型“TrainingSet”上实现了IComparable,则可以在列表上使用Sort方法。 您必须实现一个“CompareTo”方法,然后可以将其委托给双倍类型Z的“CompareTo”,这样就可以避免异常情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接