Level 42
Level 42

Reputation: 422

How do a sort this list on two keys

public class Witness
{
    public Mobile m_mobile;
    public bool m_hasLOS;
    public double m_distanceToSqrt;
    public Witness(Mobile m, bool hasLOS, double distanceToSqrt)
    {
        m_mobile = m;
        m_hasLOS = hasLOS;
        m_distanceToSqrt = distanceToSqrt;
    }
}

I've created a list of these objects 'witnesses' and now want to sort it on both m_hasLOS and m_distanceToSqrt.

In my code I call:

List<Witness> sorted = witnesses.OrderBy(x => x.m_hasLOS).ThenBy(x => x.m_distanceToSqrt).ToList();

This works, but the list is not ordered how I would like. How do I change the ordering such that: LOS is always at the top of the list, ordered on ascending distance? And where LOS is false, the list is then just ascending distance?

For example:

o1: m_LOS = true, m_distanceToSqrt = 13
o2: m_LOS = false, m_distanceToSqrt = 6
o3: m_LOS = false, m_distanceToSqrt = 2

Should yield the reaultant sort:

o1: m_LOS = true, m_distanceToSqrt = 13
o3: m_LOS = false, m_distanceToSqrt = 2
o2: m_LOS = false, m_distanceToSqrt = 6

Upvotes: 1

Views: 692

Answers (1)

D M
D M

Reputation: 7179

In the C family of languages (and many others), you can think of false as being equal to 0 and true being equal to 1 (more specifically anything that is not 0).

So sorting on a boolean field will return all false values first when the sort order is ascending because 0 < 1. If you want the true values first, you will need to use the descending sort order.

For OrderBy, that is OrderByDescending. For ThenBy, that is ThenByDescending. In your case, you just need to use the first.

List<Witness> sorted = witnesses
    .OrderByDescending(x => x.m_hasLOS)
    .ThenBy(x => x.m_distanceToSqrt)
    .ToList();

Try it out on .NET Fiddle.


Side note, public members should use pascal casing, properties should be preferred over public fields in order to abstract implementation details from class responsibilities, and variable names should favor readability over length.

The conventional way to write your class definition would be:

public class Witness
{
    private Mobile mobile;
    private bool hasLineOfSight;
    private double distanceToSquareRoot;

    public Mobile Mobile 
    {
        get => mobile;
        set => mobile = value;
    }

    public bool HasLineOfSight
    {
        get => hasLineOfSight;
        set => hasLineOfSight = value;
    }

    public double DistanceToSquareRoot
    {
        get => distanceToSquareRoot;
        set => distanceToSquareRoot = value;
    }

    public Witness(Mobile mobile, bool hasLineOfSight, double distanceToSquareRoot)
    {
        Mobile = mobile;
        HasLineOfSight = hasLineOfSight;
        DistanceToSquareRoot = distanceToSquareRoot;
    }
}

Since you don't have any methods on your class, if its primary use is as a value type, you could use the record type instead.

public record Witness(
    Mobile Mobile, 
    bool HasLineOfSight, 
    double DistanceToSquareRoot
);

Upvotes: 3

Related Questions