Home > Net >  Intersect using custom function in f#
Intersect using custom function in f#

Time:09-21

I have two lists. The first is a list of structured records that have an Id property where the Id is a string wrapper. The second is a simple list of strings.

I need to find all the records in the first list whose Id is represented in the second set.

In c# this would be trivial:

var intersect = list1.Where(x => list2.Contains(x.Id.ToString()))

Maybe there are more efficient ways to get the intersection but c# with linq makes it very easy to get the required output.

I'm fairly new to f# but it seems like this is a non-trivial operation in f#. Linq in f# doesn't support the lambda comparator function (as far as I can see) and the built in filter function seems to rely on a direct comparison between the entities in the two lists (its not easy to specify a custom comparator).

I've tried the following:

list1 |> List.filter(fun x -> List.contains (x.Id.ToString() list2))

... but this gives the following compiler error:

This expression was expected to have type
    'bool'    
but here has type
    ''a list -> bool'

I am really hoping that someone is going to tell me that I'm being dumb and there is a really easy way to do this in f#... please!

ANSWER

It turns out that I had messed up my syntax. I think that the parentheses below are required to force x.Id.ToString() to be evaluated first but the parentheses around the parameters to List.contains aren't required

list1 |> List.filter(fun x -> List.contains (x.Id.ToString()) list2)

CodePudding user response:

In F#, function arguments are separated by spaces, not commas, and the parentheses around each argument are optional. You do not have parentheses around the whole argument list - that would indicate a tuple.

You have written:

list1 |> List.filter(fun x -> List.contains (x.Id.ToString(), list2))

But you probably meant this:

list1 |> List.filter (fun x -> List.contains (x.Id.ToString()) list2)

Complete example (put in an .fsx file):

type Foo =
  {
    Id : string
  }

let list1 =
  [
    { Id = "a" }
    { Id = "b" }
    { Id = "x" }
  ]

let list2 =
  [
    "a"
    "b"
    "c"
  ]

let list3 =
  list1 |> List.filter (fun x -> List.contains (x.Id.ToString()) list2)

printfn "%A" list3

// [{ Id = "a" }; { Id = "b" }]

  • Related