Search code examples
c#linqtuples

Returning a named tuple from a LINQ query


I have been trying all day and have had many attempts but not luck. I have the following Linq query that returns results.

This query returns IEnumerable<sketchEntity>:

var temp = from entity in remainingEntities
            where entity.EndPoint.FuzzyEqual(matchPoint, _tolerance)
            select entity;

This query returns (IEnumerable<sketchEntity>, end)

var temp = (from entity in remainingEntities
            where entity.EndPoint.FuzzyEqual(matchPoint, _tolerance)
            select entity, ChainMatchType.End);

Neither of these is what I want. Each result in the final IEnumerable needs to be a named tuple with each tuple having a ISketchEntity and ChainMatchType:

List<(ISketchEntity sketchEntityName, ChainMatchType MatchName)>

I will be doing similar queries three different times.

  1. When certain types of ISketchEntities match an EndPoint.
  2. When certain types of ISketchEntities match a StartPoint.
  3. When certain types of ISketchEntities match a CenterPoint.

When I run each query I want to add the type of result using the Enum to denote the type of match.

public enum ChainMatchType
{
    Start,
    End,
    Center
}

My plan is to combine all three queries into one result before returning the result.

How can I format my LINQ to get the result into a named tuple:

Name            DataType
entity:         ISketchEntity
MatchType:      ChainMatchType

EDIT FuzzyEquals is a custom extension. It compares two points using a +/- tolerance. I am working with CAD data and past history tells me that sometimes two points can be close enough to be equal but don't have exactly the same coordinates.

    public static bool FuzzyEqual(this ISketchPoint sp, ISketchPoint other, double tolerance)
    {
        if (
            sp.X > other.X - tolerance && sp.X < other.X + tolerance &&
            sp.Y > other.Y - tolerance && sp.Y < other.Y + tolerance &&
            sp.Z > other.Z - tolerance && sp.Z < other.Z + tolerance
        )
            return true;
        return false;
    }

After seeing it just now I guess it could be simplified to:

    public static bool FuzzyEqual(this ISketchPoint sp, ISketchPoint other, double tolerance)
    {
        return sp.X > other.X - tolerance && sp.X < other.X + tolerance &&
               sp.Y > other.Y - tolerance && sp.Y < other.Y + tolerance &&
               sp.Z > other.Z - tolerance && sp.Z < other.Z + tolerance;
    }

Solution

    • I recommend using Linq with only extension-methods, and avoid the keyword-style from, in, where, select.
      • I'll admit this is a personal preference of mine - but C#'s Linq keyword feature is somewhat aneamic and you almost always end-up needing to use some extension-methods (especially ToList and Include) and mixing keyword and extension-method styles makes things harder to read.
    • Anyway, you just need to add the value-tuple in a Select step, which can be a method parameter (though you cannot easily parameterize value-tuple member names - it's possible, but out-of-scope for this question).

    Like so:

    List<( ISketchEntity sketchEntityName, ChainMatchType matchName )> withName = remainingEntities
        .Where( e => e.EndPoint.FuzzyEqual( matchPoint, _tolerance ) )
        .Select( e => ( sketchEntityName: e, matchName: matchName ) )
        .ToList()
    

    As a parameterized method:

    (I don't know what matchPoint and _tolerance are btw).

    public static List<( ISketchEntity sketchEntityName, ChainMatchType matchName )> Get2( this IEnumerable<ISketchEntity> entities, ChainMatchType matchType )
    {
        // Precondition:
        if( entities is null ) throw new ArgumentNullException(nameof(entities));
    
        return entities 
            .Where( e => e.EndPoint.FuzzyEqual( matchPoint, _tolerance ) )
            .Select( e => ( sketchEntityName: e, matchName: matchType ) )
            .ToList()
    }