确定具有特定子类型的数组中的最高,第二高,第三高值?

时间:2014-02-21 18:20:01

标签: c# arrays parent-child

我有一系列抽象类运动员。

    {
        athletes[0] = new BobSledder("Bobby", "Ben and Bill");
        athletes[1] = new BobSledder("Burt", "Bonnie and Baek");
        athletes[2] = new BobSledder("Barnie", "Blues and  Bart");
        athletes[3] = new Skier("Sally", "downhill");
        athletes[4] = new Skier("Sarah", "downhill");
        athletes[5] = new Skier("Sam", "downhill");
        athletes[6] = new IceSkater("Suzy", "blue");
        athletes[7] = new IceSkater("Sullie", "red");
        athletes[8] = new IceSkater("Sierra", "orange");

    }

运动员课程中有一个名为averageScore的字段。我为每个不同的孩子类型设有基于运动员的儿童班。我希望能够找到数组中每个子类型的前3个averageScores。如果增加更多运动员,我希望能够仍然只能找到前3名。我将如何做到这一点?对不起,我对编程很新,而且以前从未使用过子类型。我不知道从哪里开始。

 public abstract class Athlete
{
    string name;
    int eventsRun;
    int lastScore;
    int runningTotal;
    double averageScore;

    public Athlete()
    {
    }

    public Athlete(string name)
    {
        this.name = name;
    }

    public string Name { get { return name; } }
    public int EventsRun { get { return eventsRun; } }
    public int LastScore
    {
        get { return lastScore; }
        set
        {
            lastScore = value;
            eventsRun++;
            runningTotal += lastScore;
            averageScore = runningTotal / eventsRun;
        }
    }
    public double AverageScore { get { return averageScore; } }

3 个答案:

答案 0 :(得分:2)

在.NET 3.5或更高版本中:

var topThreePerSubtype = athletes.GroupBy(x => x.GetType()).Select(g => g.OrderByDescending(x => x.averageScores).Take(3));

这实际上会返回IEnumerable IEnumerable<Athlete>,这可能不是最好的。我建议您使用TopThree类或类似属性来表示生成的实例。

这是一个(过于简化的)示例:

public class TopThree
{
    public Type SubType {get; set;}
    public IEnumerable<Athlete> Athletes {get; set;}
}

(...)

var topThreePerSubtype = athletes.GroupBy(x => x.GetType()).Select(g => new TopThree() { SubType = g.Key, Athletes = g.OrderByDescending(x => x.AverageScores).Take(3)});

这种方法的优点在于即使您添加了一个全新的学科,它也能够为您创建另一个前三个对象。不需要额外的代码。

当然,因为这会使用LINQ扩展,所以你必须确保在文件的顶部有这个:

using System.Linq;

答案 1 :(得分:0)

首先,我会使用一个运动员名单,而不是阵列,因为你希望能够改变运动员的数量,只有当他们内部的数字不会改变时才能使用阵列。

那么你有:

List<Athlete> athletes = new List<Athlete>{/*Whatever athlete's you're adding at the start*/};

List<Athlete> athletes = new List<Athlete>();
athletes.Add(new BobSledder("Bobby", "Ben and Bill"));
...

这使您可以更轻松地在运行时更改运动员的数量。通过他们的运动来分离每个运动员也可能更容易,而不是将它们全部放在一个列表/阵列中,因为它最终可能更清洁或更简单,但我不确定你的最终目标是什么所以我不能确定哪种方式更好。如果这不是你需要根据他们各自的运动来对付运动员的唯一地方,你可能想要考虑将它们分成单独的列表,每个运动一个。至少要记住一些事情。

然后你可以简单地在运动员身上使用一个foreach循环,并找到前三个类似的东西:

double minScore = 0;
double maxScore = 0;
double midScore = 0;

foreach(Athlete player in Athletes){
    if (player.averageScore >= minScore) {
        minScore = midScore;
        midScore = maxScore;
        maxScore = player.averageScore; 
    } else if (player.averageScore >= midScore)
        minScore = midScore;
        midScore = player.averageScore;
    } else if (player.averageScore >= minScore);
        minScore = player.averageScore;
    }
}

虽然上面的代码只能找到所有运动员中的前三个分数,但您可以设置一组独特的分数,然后通过使用类似

的方式过滤掉它们。
if(player.GetType() == typeof(Skier))

确定要切换到的集合。如果你经常需要仅根据他们的运动来处理他们,我仍然会建议你将运动员分成运动员,因为它会让一切变得更轻松。

如果将它们拆分为单独的列表,您可以创建一个类似的函数:

private double[] getTopThreeScores(List<Athlete> inAthletes){
    //Use this instead of the min/max/mid and then access by the index
    double[3] topScores = new double{0.0,0.0,0.0};

    /*Loop from above, but switch out topScores[0] for minScore, topScores[1] for mid, etc.*/

    return topScores;
}

希望这有点帮助。

保持阵列:

private double[] getTopThreeScores(object inSport, Athlete[] inAthletes){
    Type sportType = inSport.GetType();
    double[3] topScores = new double{0.0,0.0,0.0};
    int length = inAthletes.Length;
    for(int i = 0; i < length; i++){
        if(inAthletes[i].GetType() == sportType){
            if (player.averageScore >= minScore) {
                minScore = midScore;
                midScore = maxScore;
                maxScore = player.averageScore; 
            } else if (player.averageScore >= midScore)
                minScore = midScore;
                midScore = player.averageScore;
            } else if (player.averageScore >= minScore);
                minScore = player.averageScore;
            }
        }
    }
    return topScores;
}

会被称为

getTopThreeScores(new Skier(), athletes);

或者你可以传递一名特定的运动员。

不一定是最干净的方式,但它会起作用并允许您指定不同的类型。我没有编译这个,所以可能会有一些小错误。

答案 2 :(得分:0)

你提到:

  

我希望能够找到数组中每个子类型的前3个averageScores。

所以我提出以下解决方案:

var top3Skiers = athletes.OfType<Skier>()
    .OrderByDescending(s => s.AverageScore).Take(3);

与其他解决方案一样,这需要使用System.Linq命名空间才能工作。 OfType只会选择指定类型的对象,允许您过滤掉您想要的内容。这意味着如果您想要选择前3个IceSkater,则需要指定OfType<IceSkater>()

为了枚举结果,您可以foreach覆盖它们,如下所示:

foreach (var skier in top3Skiers)
    Console.WriteLine("{0} - {1}", skier.Name, skier.AverageScore);