Tom Savage
Tom Savage

Reputation: 3182

Ranking objects in List<> by multiple criteria

I have a list of objects representing plants - each of which have member properties which represent a range of tolerable environmental conditions.

I want to re-order this list such that the plant objects are ranked in order of suitability.

I figure I want to give each item in the list a rank for temperature/pH suitability based on the proximity of the middle of their temperature/pH range to the environment data supplied.

I would then average the ranks and sort the list by this number.

However, I'm unsure what the best approach would be for tracking the ranks given to each item in the list. Does anybody have any recommendations on how best to approach this?

Upvotes: 2

Views: 3330

Answers (8)

Rup
Rup

Reputation: 34418

Assuming you want to display the various suitability criteria rather than just an ordered plant list, I think you'll need to define a class with the extra data:

class PlantRankings
{
    public Plant Plant { get; set; }
    public double PhSuitabilityScore { get; set; }
    public double SoilTypeScore { get; set; }
    public double SmellsNiceScore { get; set; }

    public double RankingScore { get; set; } // could compute on-the-fly here
}

and map your Plants into that class and then sort by the combined score there. I guess you'd write a method that accepted a Plant and your criteria then returned a ranking structure, e.g.

var rankedPlants = plants.Select(p => ScoreSuitability(p, criteria))
                         .OrderBy(pr => pr.RankingScore).ToList();

If by rank then you meant the 1-N ranking for each plant by each criteria then again I think you'll need to generate an extended structure but with

public int PhSuitabilityRank { get; set; }

alongside the scores; then once you've computed the score and built the list you can

int rank = 1;
for(var rankedPlant in
           rankedPlants.OrderByDescending(pr => pr.PhSuitabilityScore))
{
    rankedPlant.PhSuitabilityRank = rank;
    ++rank;
}

and so on. (There may be a way to do that in LINQ but without a .ForEach() on the Order results I think that's clearer and simpler.)

Upvotes: 0

Saboor Awan
Saboor Awan

Reputation: 1585

you can sort your planet list using linq or lembda expression with order by rank it will get you the sorted list.

Upvotes: 0

Guffa
Guffa

Reputation: 700680

You need a method to calculate the rank for an item, let's call it GetRank, then you can use that in the Sort method with a Comparison:

plants.Sort((x,y) => GetRank(x).CompareTo(GetRank(y)));

You can also use the OrderBy extension method which takes a delegate which returns the value for each item:

plants = plants.OrderBy(x => GetRank(x)).ToList();

The method for calculating the rank doesn't have to be a named method, you could put the code directly in the Comparison or OrderBy delegate by simply substituting GetRank(n) with any expression you like.

Upvotes: 0

Mikael Östberg
Mikael Östberg

Reputation: 17166

You could do it like this:

var plants = new Plant[] { new Plant { T = 1, Ph = 2 },  new Plant { T = 2, Ph = 2 } };

var ps = from p in plants
         let magicNumber = p.T/p.Ph // Or some other algorithm
         orderby magicNumber
         select p;

Then you can just create any number you like from the properties of a Plant and you don't have to implement a Comparer.

Upvotes: 2

BrokenGlass
BrokenGlass

Reputation: 160982

In your case it sounds like you would be best off with a custom comparer that you can pass your environment settings:

class PlantComparer : IComparer<Plant>
{
    public PlantComparer(PlantEnvironmentSettings settings)
    {

    }

    public int Compare(Plant plant1, Plant plant2)
    {
        //compare two plants, return 0, 1 or -1 
        return 0;
    }
}

Then you can use it in a sort:

List<Plant> plants = new List<Plant>();
..
PlantEnvironmentSettings settings = new PlantEnvironmentSettings();
plants.Sort(new PlantComparer(settings));

Upvotes: 0

Richard Friend
Richard Friend

Reputation: 16018

Expose the method for calculating suitability in your class, then use the LINQ OrderBy extension

public int GetSuitabilityIndex()
{
     //Calculate suitability..
}


var sorted = myList.OrderBy(item=>item.GetSuitabilityIndex());

Upvotes: 0

Tejs
Tejs

Reputation: 41256

You can use the LINQ OrderBy extension method. Now, typically you could write it like so:

 mySet.OrderBy(x => x.SomeProperty)

However, if you want to order by multiple properties, you can simply create a method to define a value:

mySet.OrderBy(x => CreateValueFor(x))

And then CreateValueFor returns back a specific value. For example, creating some integer that defines it's suitability, which is really a function of some properties on your object.

Upvotes: 0

Tergiver
Tergiver

Reputation: 14517

Can't you simply call the Sort method, passing in a Comparison method that evaluates in exactly the way you just described?

Upvotes: -1

Related Questions