kaefer
kaefer

Reputation: 5741

Functional Relational Operators, join-like, outside of queries

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

Answers (1)

N_A
N_A

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

Related Questions