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" }]