If leftOuterJoin, .DefaultIfEmpty () is not needed

Documentation for leftOuterJoin expressions on MSDN are repeatedly implied through patterns that when using leftOuterJoin .. on .. into .. that you still have to use .DefaultIfEmpty() to achieve the desired effect.

I do not think this is necessary because I get the same results in both of these tests, which differ only in the fact that the second does not matter .DefaultIfEpmty()

 type Test = A | B | C let G = [| A; B; C|] let H = [| A; C; C|] printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.DefaultIfEmpty() do select (g, i)} printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do select (g, i)} // seq [(A, A); (B, null); (C, C); (C, C)] // seq [(A, A); (B, null); (C, C); (C, C)] 

1) Can you confirm this?

If this were correct, I only realized this after writing this alternative type increase in an attempt to better cope with unsurpassed results, and I was surprised to see null in my release!

 type IEnumerable<'TSource> with member this.NoneIfEmpty = if (Seq.exists (fun _ -> true) this) then Seq.map (fun e -> Some e) this else seq [ None ] printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.NoneIfEmpty do select (g, i)} // seq [(A, Some A); (B, Some null); (C, Some C); (C, Some C)] 

2) Is there a way to get None instead of null / Some null from leftOuterJoin ?

3) What I really want to do is find out if there are any unsurpassed g

 printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.NoneIfEmpty do where (i.IsNone) exists (true) } 

I realized that this is the following, but this is not very F #:

 printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do where (box i = null) exists (true)} 
+4
source share
1 answer

Short version: query expressions use null values. This is a rough place in the language, but pent-up.

I have done this before:

 let ToOption (a:'a) = match obj.ReferenceEquals(a,null) with | true -> None | false -> Some(a) 

This will allow you to do:

 printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do select ( g,(ToOption i))} 

Which wraps each result in an option (since you don’t know whether it will be me. It is worth noting that F # uses null to represent None at run time as an optimization. Therefore, to check if this is really what you want, make a decision for this option, for example:

 Seq.iter (fun (g,h) -> printf "%A," g; match h with | Some(h) -> printfn "Some (%A)" h | None -> printfn "None") <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do select ((ToOption g),(ToOption i))} 
+3
source

Source: https://habr.com/ru/post/1257803/


All Articles