@RicoSaupe (Rico Saupe) posted a new topic, subject "Access "shared" property of elements in a list". It read:

   Hi
    
    How do I do the following?  
    I have several types. Each of the types have two properties in common.
    [code] 
        type OpsFlag =
                  Key: FlagKey
                  Project: Project
                  Metadata: FlagMetadata option
        
        type TimeBasedFlag =
                  Key: FlagKey
                  Project: Project
                  Metadata: FlagMetadata option
        
        type Flag =
                | OpsFlag of OpsFlag
                | TimeBasedFlag of TimeBasedFlag
        
        flags  // Flag list
        |> List.filter (fun f -> f.Project = project)
        
    [/code]
    
    How do i now filter a list by a property “project” that all types in “Flag” have in common? I understand inheritance from a base class with these properties is not really an option.
    
    Thank you in advance. Let me know if you need more information.

To which @xerxesb (Xerxes Battiwalla) replied

    Disclaimer: I'm new to F# myself, so there may be a better way than this, but here's my attempt.
    
    If you have a list of DUs then I think the only way to treat them “polymorphically” is to pattern match on each flag instance.
    
    Here's a solution I just tried. Seems to work. (NB: I stripped out the parts from your example not relevant to the solution)
    [code] 
        module TestFilter
        
        type Project = {
            Id: int
        }
        
        type OpsFlag = {
            Project: Project
        }
        
        type TimeBasedFlag = {
            Project: Project
        }
        
        type Flag = 
            | OpsFlag of OpsFlag
            | TimeBasedFlag of TimeBasedFlag
        
        let ops1: Flag = OpsFlag { Project = { Id = 1 }}
        let ops2: Flag = OpsFlag { Project = { Id = 2 }}
        let time1: Flag = TimeBasedFlag { Project = { Id = 1 }}
        let time2: Flag = TimeBasedFlag { Project = { Id = 2 }}
          
        let flags = [ops1; ops2; time1; time2]
        
        let whereIdIs i (f: Flag) = 
            match f with
            | OpsFlag x -> x.Project.Id
            | TimeBasedFlag x -> x.Project.Id
            |> (=) i
        
        printfn "Number of elements in list: %i" (List.length flags)
        
        flags  // Flag list
        |> List.filter (whereIdIs 2)
        |> List.length
        |> printfn "Number of elements in filtered result: %i"
    [/code]

Then @RicoSaupe (Rico Saupe) said

    Thanks for the quick reply.  
    Thats what i did already. (right after that post) But doing this in multiple places I kind of not what i wanted. There must be a way to keep it simpler.

So @Prash replied

    In this situation I would probably add a member onto `Flag` called `Project` that returns the project for each case:
    [code] 
        type Flag =
            | OpsFlag of OpsFlag
            | TimeBasedFlag of TimeBasedFlag
            member this.Project =
                match this with
                | OpsFlag f -> f.Project
                | TimeBasedFlag f -> f.Project
        
    [/code]
    
    Then your filter code will work as you have written it, as long as the compiler already knows that the type of `f` is `Flag`. It won't automatically infer that from calling `.Project` in the way that it does for a normal record field.
    
    This works if every case has a project. If any cases don't have a project then the member will need to return an option.

Then @RicoSaupe (Rico Saupe) said

    Thanks Prash. It couldn't be simpler.   
    This saves me a lot of noise in the code.


{'source': 'discourse', 'url': 'https://forums.fsharp.org/t/access-shared-property-of-elements-in-a-list/1492', 'title': 'Access "shared" property of elements in a list', 'views': 330, 'has_accepted_answer': True, 'word_count': 441, 'like_count': 2, 'reply_count': 2, 'participant_count': 3, 'posts': 5, 'tags': 'beginners', 'author': '@RicoSaupe (Rico Saupe)', 'participants': '@RicoSaupe (Rico Saupe), @xerxesb (Xerxes Battiwalla), @Prash', 'image_url': [None], 'created_at': '2020-10-19T11:46:46.091Z', 'last_posted_at': '2020-10-21T11:22:28.274Z'}