Access "shared" property of elements in a list

Hi

How do I do the following?
I have several types. Each of the types have two properties in common.

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)

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.

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)

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"

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.

In this situation I would probably add a member onto Flag called Project that returns the project for each case:

type Flag =
    | OpsFlag of OpsFlag
    | TimeBasedFlag of TimeBasedFlag
    member this.Project =
        match this with
        | OpsFlag f -> f.Project
        | TimeBasedFlag f -> f.Project

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.

1 Like

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

1 Like