This expression correctly evaluates to 225.58:
let alfki = ctx.``[dbo].[Customers]``.Individuals.ALFKI
let changeNoneToZero x = match x with | Some v -> v | None -> 0m
let freights = alfki.FK_Orders_Customers |> Seq.map (fun x -> changeNoneToZero x.Freight) |> Seq.sum
corresponding to the following database query:
select SUM(freight)
from dbo.Orders
where CustomerID = (
select CustomerID from Customers
where CustomerID = 'ALFKI')
Is there some built in equivalent or close analogue of the changeNoneToZero function? Could I be seeking to Seq.sumBy something instead?
#ildjarn's approach of using Seq.choose works. Instead of converting None to 0m and then summing everything, the following selects only those values that are not None and pipes them into Seq.sum:
let freights = alfki.FK_Orders_Customers
|> Seq.choose (fun x -> x.Freight)
|> Seq.sum
Thanks #ildjarn!
You can also use Option.fold:
alfki.FK_Orders_Customers |> Option.fold (fun _ a -> a) 0m
And you can make it generic:
let inline valOrZero ov = ov |> Option.fold (fun _ a -> a) LanguagePrimitives.GenericZero
Related
A quick question on how to effectively group/filter list/seq.
Filter for only records where the optional field is not None
Remove the "option" parameter to make future processes easier (as None has been filtered out)
Group (this is of no problem I believe)
Am I using the best approach?
Thanks!
type tmp = {
A : string
B : int option }
type tmp2 = {
A : string
B : int }
let inline getOrElse (dft: 'a) (x: 'a option) =
match x with
| Some v -> v
| _ -> dft
let getGrouped (l: tmp list) =
l |> List.filter (fun a -> a.B.IsSome)
|> List.map (fun a -> {A = a.A ; B = (getOrElse 0 (a.B)) })
|> List.groupBy (fun a -> a.A)
The most natural approach for map+filter when option is involved is to use choose, which combines those two operations and drops the option wrapper from the filtered output.
Your example would look something like this:
let getGrouped (l: tmp list) =
l
|> List.choose (fun a ->
a.B
|> Option.map (fun b -> {A = a.A; B = b})
|> List.groupBy (fun a -> a.A)
The simple solution is just use the property that an option can be transformed to list with one or zero elements then you can define a function like:
let t1 ({A=a; B=b} : tmp) =
match b with
| (Some i) -> [{ A = a; B= i}]
| _ -> []
let getGrouped (l: tmp list) =
l |> List.collect t1
|> List.groupBy (fun a -> a.A)
this solution
d1 |> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
from this so answer
Finding the difference between two arrays in FSharp
gives this error
Severity Code Description Project File Line Suppression State
Error Type mismatch. Expecting a
unit -> bool
but given a
'a [] -> bool
The type 'unit' does not match the type ''a []' ParseLibs
Program.fs 25
Full code:
// Learn more about F# at http://fsharp.org
// See the 'F# Tutorial' project for more help.
open System
open System.IO
open FSharp.Collections
[<EntryPoint>]
let main argv =
let path = "data1.txt"
let lines = use reader = new StreamReader(path) in reader.ReadToEnd().Split('\n')
let n = 5
let d1 = lines
|> Array.takeWhile (fun e -> not (e.Equals "\r"))
let d2 = lines
|> Array.skipWhile (fun e -> not (e.Equals "\r"))
|> Array.skip 1
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
|> Array.iter (fun (e, i) -> printfn "%s" e)
d1 |> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
//let writer = new StreamWriter(path)
ignore (Console.ReadKey())
0 // return an integer exit code
Is the answer there wrong? What is the real answer? I am simply trying to filter all the elements that are in both arrays. In most functional languages this is as trivial as they come.
d1 |> Array.filter (fun t -> d2.contains(t))
The problem is that d2 has type unit.
As array.iter returns ()
I would change to
let d2 = lines
|> Array.skipWhile (fun e -> not (e.Equals "\r"))
|> Array.skip 1
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
d1
|> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
|> Array.iter (fun (e, i) -> printfn "%s" e)
Using the actual answer from the above link: https://stackoverflow.com/a/28682277/5514938 and adding the information at https://en.wikipedia.org/wiki/Set_theory#Basic_concepts_and_notation
the following code is an example of the three first concepts with same sets/values as in wiki article.
let d1= [|"1";"2";"3";|] //pretend this to be the filtered/skipped/mapped
//whatever but otherwise "clean" structure/datatypes
let d2 = [|"2";"3";"4";|] //pretend this to be the filtered/skipped/mapped
//whatever but otherwise "clean" structure/datatypes
//equal to d1
let s1 = d1
|> Set.ofArray
let s2 = d2
|> Set.ofArray
let all = s1 + s2 //Union
let inBoth = Set.intersect s1 s2 //Intersection
let onlyIn_d1 = s1 - s2 //Set difference
let onlyIn_d2 = s2 - s1 //Set difference the other way ;-)
I have removed your other code to simplify the concepts, so the initial filter, skippings and mappings you are doing must of course be done ahead of the above code. And you must also "realign" the types to be equal again:
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
|> Array.map (fun (e,i) -> e)
|> Set.ofArray
I'm trying to do some mathematical operations with decimal options on a custom type:
type LineItem = {Cost: decimal option; Price: decimal option; Qty: decimal option}
let discount = 0.25M
let createItem (c, p, q) =
{Cost = c; Price = p; Qty = q}
let items =
[
(Some 1M , None , Some 1M)
(Some 3M , Some 2.0M , None)
(Some 5M , Some 3.0M , Some 5M)
(None , Some 1.0M , Some 2M)
(Some 11M , Some 2.0M , None)
]
|> List.map createItem
I can do some very simple arithmetic with
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x))
which gives me
val it : seq<decimal option> =
seq [null; Some 0.500M; Some 0.750M; Some 0.250M; ...]
If I try to actually calculate the thing I need
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - (line.Cost
|> Option.map (fun x -> x)))
|> Option.map (fun x -> x * (line.Qty
|> Option.map (fun x -> x))))
I get the error
error FS0001: Type constraint mismatch. The type
'a option
is not compatible with type
decimal
The type ''a option' is not compatible with the type 'decimal'
where I would have expected a seq<decimal option>.
I must be missing something but I can't seem to spot whatever it is I'm missing.
You are mixing decimal with decimal option.
If you're trying to solve everything with Option.map you may want to try with Option.bind instead, so your code will be 'linearly nested':
items
|> Seq.map (
fun line ->
Option.bind(fun price ->
Option.bind(fun cost ->
Option.bind(fun qty ->
Some ((discount * price - cost ) * qty)) line.Qty) line.Cost) line.Price)
which can be an interesting exercise, especially if you want to understand monads, then you will be able to use a computation expression, you can create your own or use one from a library like F#x or F#+:
open FSharpPlus.Builders
items |> Seq.map (fun line ->
monad {
let! price = line.Price
let! cost = line.Cost
let! qty = line.Qty
return ((discount * price - cost ) * qty)
}
)
but if you link F#+ you'll have Applicative Math Operators available:
open FSharpPlus.Operators.ApplicativeMath
items |> Seq.map (fun line -> ((discount *| line.Price) |-| line.Cost ) |*| line.Qty)
That's nice stuff to learn but otherwise I would suggest to use F# built-in features instead, like pattern-match, it would be easier:
items
|> Seq.map (fun line -> match line.Price, line.Qty, line.Cost with
| Some price, Some qty, Some cost ->
Some ((discount * price - cost ) * qty)
| _ -> None)
Then since you can also pattern-match over records it can be further reduced to:
items
|> Seq.map (function
| {Cost = Some cost; Price = Some price; Qty = Some qty} ->
Some ((discount * price - cost ) * qty)
| _ -> None)
Note that Option.map (fun x -> x) doesn't transform anything.
One problem you have is the following code:
(line.Cost |> Option.map (fun x -> x))
The lambda function (fun x -> x) already exists. This is the id function. It just returns whatever you have unchanged. You also could write the code you have like this:
(line.Cost |> Option.map id)
And the next thing. Mapping over the id function makes no sense. You unwrap whatever is inside the option, apply the id function to it. What didn't change the decimal at all. Then you wrap the decimal again in an option. You also can just write:
line.Cost
and completely remove the Option.map as it does nothing.
So the code you have here:
|> Option.map (fun x -> x - (line.Cost |> Option.map (fun x -> x)))
is identical to:
|> Option.map (fun x -> x - line.Cost)
This obviously does not work because here you try to subtract x a decimal with line.Cost a option decimal. So you get a type-error.
I guess what you really want to do is to subtract line.Cost from line.Price if line.Cost is present, otherwise you want to preserve line.Price unchanged.
One way would be just to provide a default value for line.Costs that can be used and have no effect on the subtraction. For example you could use the value 0 for subtraction if line.Costs is None.
So you also could write something like this instead:
|> Option.map (fun x -> x - (defaultArg line.Cost 0m))
A default value for multiplication would be 1m. So you overall end with.
items
|> Seq.map (fun line ->
line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - (defaultArg line.Cost 0m))
|> Option.map (fun x -> x * (defaultArg line.Qty 1m)))
The above code for example returns:
[None; Some -2.500M; Some -21.250M; Some 0.500M; Some -10.500M]
If your goal is that a whole computation turns into None as soon a single
value is None. I would just add map2 as a helper function.
module Option =
let map2 f x y =
match x,y with
| Some x, Some y -> Some (f x y)
| _ -> None
then you just can write:
items
|> List.map (fun line ->
line.Price
|> Option.map (fun price -> price * discount)
|> Option.map2 (fun cost price -> price - cost) line.Cost
|> Option.map2 (fun qty price -> price * qty) line.Qty)
and it will return:
[None; None; Some -21.250M; None; None]
Within the Option.map the x is actually a decimal, but the signature for Option.map is 'T option -> 'U option. So here:
Option.map (fun x -> x - (line.Cost |> Option.map (fun x -> x)))
you have the following:
Option.map (fun x -> /*decimal*/ x - /*decimal option*/(line.Cost |> Option.map (fun x -> x)))
So the decimal option has to be converted to decimal to be compatible with what's in the first Option.map. But now you have to deal with None result.
Below a quick (and nasty) fix that just is using an if statement to extract Value (which will be a decimal) or if None then return 0.
items
|> Seq.map (fun line -> line.Price
|> Option.map (fun x -> discount * x)
|> Option.map (fun x -> x - if line.Cost.IsSome then line.Cost.Value else 0m)
|> Option.map (fun x -> x * if line.Qty.IsSome then line.Qty.Value else 0m))
For a more sophisticated solution I recommend this answer.
For completeness sake, you may also leverage the monadic properties of the option type by "lifting" the values outside of the option. This is a somewhat simpler variant of the applicative approach linked by #PiotrWolkowski and those shown by #Gustavo. Applicatives not only wrap values in the monad, but also the functions applied to them.
We start by making the option type amenable to monadic operations in terms of bind and return. Thankfully, those functions are already defined, there's only a little tweak in the argument order.
let (>>=) ma f = Option.bind f ma
let ``return`` = Some
On top of this there's the lift function and a couple of operators for convenience. If needed, they could be generalized by marking them inline.
let liftOpt op x y =
x >>= fun a ->
y >>= fun b ->
``return`` (op a b)
let (.*) = liftOpt (*)
let (.-) = liftOpt (-)
Now your calculation becomes
items
|> Seq.map (fun line ->
(line.Price .* Some discount .- line.Cost) .* line.Qty )
|> Seq.iter (printfn "%A")
which will print
<null>
<null>
Some -21.250M
<null>
<null>
let private GetDrives = seq{
let all=System.IO.DriveInfo.GetDrives()
for d in all do
//if(d.IsReady && d.DriveType=System.IO.DriveType.Fixed) then
yield d
}
let valid={'A'..'Z'}
let rec SearchRegistryForInvalidDrive (start:RegistryKey) = seq{
let validDrives=GetDrives |> Seq.map (fun x -> x.Name.Substring(0,1))
let invalidDrives= Seq.toList validDrives |> List.filter(fun x-> not (List.exists2 x b)) //(List.exists is the wrong method I think, but it doesn't compile
I followed F#: Filter items found in one list from another list but could not apply it to my problem as both the solutions I see don't seem to compile. List.Contains doesn't exist (missing a reference?) and ListA - ListB doesn't compile either.
open System.IO
let driveLetters = set [ for d in DriveInfo.GetDrives() -> d.Name.[0] ]
let unused = set ['A'..'Z'] - driveLetters
Your first error is mixing between char and string, it is good to start with char:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0])
Now invalid drive letters are those letters which are in all but not in validDrives:
let invalidDrives =
all |> Seq.filter (fun c -> validDrives |> List.forall ((<>) c))
Since validDrives is traversed many times to check for membership, turning it to a set is better in this example:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0]) |> Set.ofSeq
let invalidDrives = all |> Seq.filter (not << validDrives.Contains)
I should split seq<a> into seq<seq<a>> by an attribute of the elements. If this attribute equals by a given value it must be 'splitted' at that point. How can I do that in FSharp?
It should be nice to pass a 'function' to it that returns a bool if must be splitted at that item or no.
Sample:
Input sequence: seq: {1,2,3,4,1,5,6,7,1,9}
It should be splitted at every items when it equals 1, so the result should be:
seq
{
seq{1,2,3,4}
seq{1,5,6,7}
seq{1,9}
}
All you're really doing is grouping--creating a new group each time a value is encountered.
let splitBy f input =
let i = ref 0
input
|> Seq.map (fun x ->
if f x then incr i
!i, x)
|> Seq.groupBy fst
|> Seq.map (fun (_, b) -> Seq.map snd b)
Example
let items = seq [1;2;3;4;1;5;6;7;1;9]
items |> splitBy ((=) 1)
Again, shorter, with Stephen's nice improvements:
let splitBy f input =
let i = ref 0
input
|> Seq.groupBy (fun x ->
if f x then incr i
!i)
|> Seq.map snd
Unfortunately, writing functions that work with sequences (the seq<'T> type) is a bit difficult. They do not nicely work with functional concepts like pattern matching on lists. Instead, you have to use the GetEnumerator method and the resulting IEnumerator<'T> type. This often makes the code quite imperative. In this case, I'd write the following:
let splitUsing special (input:seq<_>) = seq {
use en = input.GetEnumerator()
let finished = ref false
let start = ref true
let rec taking () = seq {
if not (en.MoveNext()) then finished := true
elif en.Current = special then start := true
else
yield en.Current
yield! taking() }
yield taking()
while not (!finished) do
yield Seq.concat [ Seq.singleton special; taking()] }
I wouldn't recommend using the functional style (e.g. using Seq.skip and Seq.head), because this is quite inefficient - it creates a chain of sequences that take value from other sequence and just return it (so there is usually O(N^2) complexity).
Alternatively, you could write this using a computation builder for working with IEnumerator<'T>, but that's not standard. You can find it here, if you want to play with it.
The following is an impure implementation but yields immutable sequences lazily:
let unflatten f s = seq {
let buffer = ResizeArray()
let flush() = seq {
if buffer.Count > 0 then
yield Seq.readonly (buffer.ToArray())
buffer.Clear() }
for item in s do
if f item then yield! flush()
buffer.Add(item)
yield! flush() }
f is the function used to test whether an element should be a split point:
[1;2;3;4;1;5;6;7;1;9] |> unflatten (fun item -> item = 1)
Probably no the most efficient solution, but this works:
let takeAndSkipWhile f s = Seq.takeWhile f s, Seq.skipWhile f s
let takeAndSkipUntil f = takeAndSkipWhile (f >> not)
let rec splitOn f s =
if Seq.isEmpty s then
Seq.empty
else
let pre, post =
if f (Seq.head s) then
takeAndSkipUntil f (Seq.skip 1 s)
|> fun (a, b) ->
Seq.append [Seq.head s] a, b
else
takeAndSkipUntil f s
if Seq.isEmpty pre then
Seq.singleton post
else
Seq.append [pre] (splitOn f post)
splitOn ((=) 1) [1;2;3;4;1;5;6;7;1;9] // int list is compatible with seq<int>
The type of splitOn is ('a -> bool) -> seq<'a> -> seq>. I haven't tested it on many inputs, but it seems to work.
In case you are looking for something which actually works like split as an string split (i.e the item is not included on which the predicate returns true) the below is what I came up with.. tried to be as functional as possible :)
let fromEnum (input : 'a IEnumerator) =
seq {
while input.MoveNext() do
yield input.Current
}
let getMore (input : 'a IEnumerator) =
if input.MoveNext() = false then None
else Some ((input |> fromEnum) |> Seq.append [input.Current])
let splitBy (f : 'a -> bool) (input : 'a seq) =
use s = input.GetEnumerator()
let rec loop (acc : 'a seq seq) =
match s |> getMore with
| None -> acc
| Some x ->[x |> Seq.takeWhile (f >> not) |> Seq.toList |> List.toSeq]
|> Seq.append acc
|> loop
loop Seq.empty |> Seq.filter (Seq.isEmpty >> not)
seq [1;2;3;4;1;5;6;7;1;9;5;5;1]
|> splitBy ( (=) 1) |> printfn "%A"