Reputation: 5741
Let's say I have two ordered collections of key-value-pairs, and I need to perform an inner equi-join on them to produce pairs of values: seq<'a * 'b> -> seq<'a * 'c> -> seq<'b * 'c>
.
This is easily enough wired to the extension methods of System.Linq.Enumerable
.
let foo abs acs =
System.Linq.Enumerable.Join(abs, acs,
(fun ab -> fst ab), (fun ac -> fst ac),
fun ab ac -> snd ab, snd ac )
foo ["A",1;"B",2] ["B",1;"C",2;"B",3]
// val it : System.Collections.Generic.IEnumerable<int * int> =
// seq [(2, 1); (2, 3)]
Translation of that functionality to F# is not that straightforward. To avoid filtering on the Cartesian product, I'm probably loosing some laziness/execution on demand.
let bar abs acs =
let d = dict(Seq.groupBy fst acs) in seq{
for a, b in abs do
let ok, cs = d.TryGetValue a
if ok then for c in cs -> b, snd c }
bar ["A",1;"B",2] ["B",1;"C",2;"B",3]
// val it : seq<int * int> = seq [(2, 1); (2, 3)]
Yet it feels crufty, like a hand-forged, bespoke solution to a particular sub-problem. How can I approach relational operators, possibly also natural, semi-, anti- or outer joins, in a more general, abstract way?
Upvotes: 3
Views: 61
Reputation: 19897
You can use a query expression to achieve the same thing.
query {
for a in ["A",1;"B",2] do
join b in ["B",1;"C",2;"B",3] on (fst a = fst b)
select (snd a, snd b) };;
val it : seq<int * int> = seq [(2, 1); (2, 3)]
Alternatively, there is nothing wrong with just using System.Linq.Enumerable.Join
.
Upvotes: 3