How to use map functions? - f#

So I have many types that have been defined and functions which mapped data into these records/types. Now I need to map the contents of each record/type into a "master" record which will contain everything from previous records based on a key which in this case is State. I am not sure how to go about doing this, but I have my attempt down below, along with more code to provide context. Any suggestions? Please comment if you need more information.
I have types that are defined as so:
type StateEdu =
{ State : string
Education : int
Income : float }
type StateFamily =
{ State : string
PctMoreThan4Children : float
PctFamilyMorethan3 : float }
But now I need to do something LIKE THIS (This did not work when I ran it):
let stateall = statemap.Keys
let statedatamap =
stateall
|> Seq.map (fun state ->
state,
{State = state
StateEdu = StateEdu.[state]
StateFamily = StateFamily.[state]
})
|> Map.ofSeq

It's not exactly clear what is your desired output as you're missing both variables and types. Assuming you want a new map/dictionary that contains the key of a state, and then a record which has the both the education state and the family state, one way would be is this:
(I added the types and fixed some errors)
type StateEdu =
{ State : string
Education : int
Income : float }
type StateFamily =
{ State : string
PctMoreThan4Children : float
PctFamilyMorethan3 : float }
//I added this new type so this is value of the final map.
type State2 =
{ State : string
StateEdu : StateEdu
StateFamily : StateFamily }
//Instead providing the csv it's better to minimize the example, so here are the two record lists.
let stateEdu =
[{State = "TX"; Income = 51522.; Education = 0}
{State = "AL"; Income = 6481.; Education = 1}
{State = "MO"; Income = 78921.; Education = 1}
{State = "TN"; Income = 12000.; Education = 4}
{State = "PA"; Income = 79850.; Education = 2}
{State = "NY"; Income = 79215.; Education = 1}
{State = "CA"; Income = 79045.; Education = 2}]
let datafamily =
[{State = "TX"; PctMoreThan4Children = 51.52; PctFamilyMorethan3 = 65.0}
{State = "AL"; PctMoreThan4Children = 64.00; PctFamilyMorethan3 = 51.4}
{State = "MO"; PctMoreThan4Children = 78.92; PctFamilyMorethan3 = 25.1}
{State = "TN"; PctMoreThan4Children = 12.00; PctFamilyMorethan3 = 62.1}
{State = "PA"; PctMoreThan4Children = 8.50; PctFamilyMorethan3 = 41.2}
{State = "NY"; PctMoreThan4Children = 25.15; PctFamilyMorethan3 = 31.0}
{State = "CA"; PctMoreThan4Children = 79.5; PctFamilyMorethan3 = 50.5}]
let stateedu =
stateEdu
|> Seq.map (fun x -> x.State,x)
|> Map.ofSeq
let datafam =
datafamily
|> Seq.map (fun x -> x.State,x)
|> Map.ofSeq
//This is one way to quickly extract the keys
let stateall = stateedu |> Map.toSeq |> Seq.map fst
//We go through all the keys
let statedatamap =
stateall
|> Seq.map (fun state ->
state,
{State = state //this is the State2type
StateEdu = stateedu.[state]
StateFamily = datafam.[state]
})
|> Map.ofSeq
Output:
val statedatamap : Map = map
[("AL", {State = "AL";
StateEdu = {State = "AL";
Education = 1;
Income = 6481.0;};
StateFamily = {State = "AL";
PctMoreThan4Children = 64.0;
PctFamilyMorethan3 = 51.4;};});
("CA", {State = "CA";
StateEdu = {State = "CA";
Education = 2;
Income = 79045.0;};
StateFamily = {State = "CA";
PctMoreThan4Children = 79.5;
PctFamilyMorethan3 = 50.5;};});
("MO", {State = "MO";
StateEdu = {State = "MO";
Education = 1;
Income = 78921.0;};
StateFamily = {State = "MO";
PctMoreThan4Children = 78.92;
PctFamilyMorethan3 = 25.1;};});
("NY", {State = "NY";
StateEdu = {State = "NY";
Education = 1;
Income = 79215.0;};
StateFamily = {State = "NY";
PctMoreThan4Children = 25.15;
PctFamilyMorethan3 = 31.0;};});
("PA", {State = "PA";
StateEdu = {State = "PA";
Education = 2;
Income = 79850.0;};
StateFamily = {State = "PA";
PctMoreThan4Children = 8.5;
PctFamilyMorethan3 = 41.2;};});
("TN", {State = "TN";
StateEdu = {State = "TN";
Education = 4;
Income = 12000.0;};
StateFamily = {State = "TN";
PctMoreThan4Children = 12.0;
PctFamilyMorethan3 = 62.1;};});
("TX", {State = "TX";
StateEdu = {State = "TX";
Education = 0;
Income = 51522.0;};
StateFamily = {State = "TX";
PctMoreThan4Children = 51.52;
PctFamilyMorethan3 = 65.0;};})]
If necessary you can define your output type to include both records as one item. You can always index to the record by statedu.[state].Education for example.

Related

With Elmish.wpf/F#, how to display in WPF a string option as the string or null, not "Some(string)"?

I am a newbie to F#. In WPF, I am using DisplayMemberBinding within a Datagrid as:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:AppointmentListView ItemsSource="{Binding Columns[0].AppointmentKeys}" Height="140" Background="Bisque">
<ListView.View>
<GridView>
<GridViewColumn Header="First" DisplayMemberBinding="{Binding FirstName}" Width="100"/>
<GridViewColumn Header="Last" DisplayMemberBinding="{Binding LastName}" Width="120"/>
<GridViewColumn Header="BirthDate" DisplayMemberBinding="{Binding BirthDate, StringFormat=d}" Width="100"/>
</GridView>
</ListView.View>
</local:AppointmentListView>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
The (complete) backing F# module (in Elmish.wpf) is:
module MyDataGrid.DataGrid
open Elmish
open Elmish.WPF
open System
type Visit =
{ ServiceTime: DateTime option
DoNotSee: Boolean option
ChartNumber: int option
LastName: string option
FirstName: string option
Mi: string option
BirthDate: DateTime option
PostingTime: DateTime option
AppointmentTime: DateTime option }
type Cell =
{RowNumber: int
ColumnNumber: int
AppointmentKeys: Visit list
ColumnTime: TimeSpan
AppointmentCount: int
AppointmentTime: DateTime option // all lines in the cell have the same appointment time.
}
let SetCell (rowNumber: int, columnNumber: int) =
let AppointmentsPerCell = 4
{RowNumber = rowNumber
ColumnNumber = columnNumber
AppointmentKeys = [for x in 1 .. AppointmentsPerCell ->
{
ServiceTime = Some System.DateTime.Now
DoNotSee = Some false
ChartNumber = Some 8812
LastName= Some ("LastName" + string x)
FirstName= Some ("FirstName" + string x)
Mi = Some "J"
BirthDate = Some(DateTime(2020,09,14))
PostingTime = Some DateTime.Now
AppointmentTime = Some DateTime.Now
}]
ColumnTime = System.TimeSpan.FromMinutes(float(columnNumber * 15))
AppointmentCount = 4
AppointmentTime = Some(DateTime.Now)
}
type Row =
{RowTime: string
Columns: Cell list}
let SetRow (rowNumber: int, startTime: System.TimeSpan)=
let columnCount = 4
let hr = System.TimeSpan.FromHours(1.0)
let rowTime = startTime + System.TimeSpan.FromTicks(hr.Ticks * int64(rowNumber))
{ RowTime = rowTime.ToString("h':00'")
Columns = [for columnNumber in 1 .. columnCount -> SetCell(rowNumber, columnNumber) ]
}
type Model =
{ AppointmentDate: DateTime
Rows: Row list
SelectedRow: Row option}
type Msg =
| SetAppointmentDate of DateTime
| SetSelectedRow of Row option
let init =
let rowCount = 9
let startTime = TimeSpan.FromHours(float(8))
{ AppointmentDate = DateTime.Now
Rows = [for rowNumber in 0 .. rowCount -> SetRow(rowNumber, startTime)]
SelectedRow = None
}
let update msg m =
match msg with
| SetAppointmentDate d -> {m with AppointmentDate = d}
| SetSelectedRow r -> {m with SelectedRow = r}
let bindings () : Binding<Model, Msg> list = [
"SelectedAppointmentDate" |> Binding.twoWay( (fun m -> m.AppointmentDate), SetAppointmentDate)
"Rows" |> Binding.oneWay( fun m -> m.Rows)
"SelectedRow" |> Binding.twoWay( (fun m -> m.SelectedRow), SetSelectedRow)
]
let designVm = ViewModel.designInstance init (bindings ())
let main window =
Program.mkSimpleWpf (fun () -> init) update bindings
|> Program.withConsoleTrace
|> Program.runWindowWithConfig
{ ElmConfig.Default with LogConsole = true; Measure = true }
window
The DisplayMememberBindings show LastName as "Some(LastName1)" and BirthDate as "Some(09/14/2020 00:00:00)".
How can I get the LastName: string option to return either null or the value of the string so the display shows "LastName1" and not "Some(LastName1)?
The same goes for the birth date, how to show BirthDate as "9/14/2020" and not "Some(09/14/2020 00:00:00)?
TIA
Full source code at: Example DataGrid
Your code only has three bindings. You should have a binding for every individual piece of data. Specifically, you should change your Rows binding from a OneWay binding to a SubModel binding. Then repeat this for all your other types.
Then, the question you specifically asked about is how to display LastName1 instead of Some(LastName1) and 9/14/2020 instead of Some(09/14/2020 00:00:00). Create the bindings for these individual pieces of optional data with Binding methods that ends in Opt like Binding.oneWayOpt or Binding.twoWayOpt.
For newbies like me, here is my full F# working solution in Elmish.WPF/F#:
module MyDataGrid.DataGrid
open Elmish
open Elmish.WPF
open System
module Visit =
type Model =
{ ServiceTime: DateTime option
DoNotSee: Boolean option
ChartNumber: int option
LastName: string option
FirstName: string option
Mi: string option
BirthDate: DateTime option
PostingTime: DateTime option
AppointmentTime: DateTime option
Id: int}
let SetVisits appointmentsPerCell = [for x in 1 .. appointmentsPerCell ->
{
ServiceTime = Some System.DateTime.Now
DoNotSee = Some false
ChartNumber = Some 8812
LastName= Some ("LastName" + string x)
FirstName= Some ("FirstName" + string x)
Mi = Some "J"
BirthDate = Some(DateTime(2020,09,14))
PostingTime = Some DateTime.Now
AppointmentTime = Some DateTime.Now
Id = x
}]
let bindings() = [
"FirstName" |> Binding.oneWayOpt( fun (_, m) -> m.FirstName)
"LastName" |> Binding.oneWayOpt( fun (_, m) -> m.LastName)
"BirthDate" |> Binding.oneWayOpt( fun (_, m) -> m.BirthDate)
"ServiceTime" |> Binding.oneWayOpt( fun (_, m) -> m.ServiceTime)
]
module Cell =
type Model =
{ RowNumber: int
ColumnNumber: int
AppointmentKeys: Visit.Model list
ColumnTime: TimeSpan
AppointmentCount: int
AppointmentTime: DateTime option // all lines in the cell have the same appointment time.
Id: int
}
let SetCell (rowNumber: int, columnNumber: int) =
let AppointmentsPerCell = 4
{RowNumber = rowNumber
ColumnNumber = columnNumber
AppointmentKeys = Visit.SetVisits AppointmentsPerCell
ColumnTime = System.TimeSpan.FromMinutes(float(columnNumber * 15))
AppointmentCount = 4
AppointmentTime = Some(DateTime.Now)
Id=rowNumber*10 + columnNumber
}
let bindings() =[
"AppointmentKeys" |> Binding.subModelSeq(
(fun (_, m) -> m.AppointmentKeys),
(fun v -> v.Id),
Visit.bindings
)
]
module Row =
type Model =
{ RowTime: string
Columns: Cell.Model list
Id: int }
let SetRow (rowNumber: int, startTime: System.TimeSpan)=
let columnCount = 4
let hr = System.TimeSpan.FromHours(1.0)
let rowTime = startTime + System.TimeSpan.FromTicks(hr.Ticks * int64(rowNumber))
{ RowTime = rowTime.ToString("h':00'")
Columns = [for columnNumber in 1 .. columnCount -> Cell.SetCell(rowNumber, columnNumber) ]
Id = rowNumber
}
let bindings () = [
"RowTime" |> Binding.oneWay( fun (_,r) -> r.RowTime)
"Columns" |> Binding.subModelSeq(
(fun (_, m) -> m.Columns),
(fun c -> c.Id),
Cell.bindings
)
]
type Model =
{ AppointmentDate: DateTime
Rows: Row.Model list
SelectedRow: Row.Model option}
type Msg =
| SetAppointmentDate of DateTime
| SetSelectedRow of Row.Model option
let init () =
let rowCount = 9
let startTime = TimeSpan.FromHours(float(8))
{ AppointmentDate = DateTime.Now
Rows = [for rowNumber in 0 .. rowCount -> Row.SetRow(rowNumber, startTime)]
SelectedRow = None
}
let update msg m =
match msg with
| SetAppointmentDate d -> {m with AppointmentDate = d}
| SetSelectedRow r -> {m with SelectedRow = r}
let bindings () : Binding<Model, Msg> list = [
"SelectedAppointmentDate" |> Binding.twoWay( (fun m -> m.AppointmentDate), SetAppointmentDate)
"Rows" |> Binding.subModelSeq(
(fun m -> m.Rows),
(fun r -> r.Id),
Row.bindings
)
"SelectedRow" |> Binding.twoWay( (fun m -> m.SelectedRow), SetSelectedRow)
]
let main window =
Program.mkSimpleWpf init update bindings
|> Program.withConsoleTrace
|> Program.runWindowWithConfig
{ ElmConfig.Default with LogConsole = true; Measure = true }
window
enter code here

Join 2 list of recoreds based on same label in f#

I have 2 lists of records with one same label id1. I need a way to join them.
type A = { id1: int; name: string }
type B = { id1: int; id2: Option<int> }
let a1 = { id1 = 1; name = "nafis" }
let a2 = { id1 = 2; name = "habib" }
let b1 = { id1 = 1; id2 = Some(5) }
let b2 = { id1 = 1; id2 = None }
let b3 = { id1 = 2; id2 = None }
let a = [a1; a2]
let b = [b1; b2; b3]
printfn "%A" a => [({id1 = 1;name = "nafis";}, {id1 = 2;name = "habib";})]
printfn "%A" b =>
[({id1 = 1; id2 = Some 5;}, {id1 = 1; id2 = None;}, {id1 = 2;id2 = None;})]
How can I join this 2 lists based on id1?
I want an output like this =>
[({id1 = 1;name = "nafis"; id2 = [Some 5; None];}, {id1 = 2;name = "habib"; id2 =[None];})]
Some form of tutorial or blog link will be helpful.
MSDN: Query expressions enable you to query a data source and put the data in a desired form. Query expressions provide support for LINQ in F#.
As correctly mentioned in the comments, you need to have a third type C that will host the joined result and then use good old LINQ to join the lists:
type A = { id1: int; name: string }
type B = { id1: int; id2: Option<int> }
type C = { id1: int; name: string; id2: Option<int> }
let a1 = { id1 = 1; name = "nafis" }
let a2 = { id1 = 2; name = "habib" }
let b1 = { id1 = 1; id2 = Some(5) }
let b2 = { id1 = 1; id2 = None }
let b3 = { id1 = 2; id2 = None }
let a = [a1; a2]
let b = [b1; b2; b3]
let result (a:A list) (b: B list) = query {
for list1 in a do
join list2 in b on
(list1.id1 = list2.id1)
select {id1 = list1.id1; name = list1.name; id2= list2.id2}
}
let c = result a b |> List.ofSeq
Result:
val c : C list = [{id1 = 1;
name = "nafis";
id2 = Some 5;}; {id1 = 1;
name = "nafis";
id2 = None;}; {id1 = 2;
name = "habib";
id2 = None;}]

Return a list from recursive function

I'm trying to return a list from a function, but I'm getting an error that says that an unit was expected instead. Also, I would like to know if this code appears to be structured correctly in general.
code:
let rec calculateVariants (attList: NewProductAttributeInfo list) (activeCount: int)
(currentList: (int * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
// define mutable list
let mutable stageList = currentList
// begin iteration
for catId,details in attGrouped do
for d in details do
if activeCount = 0
then stageList <- (activeCount,d) :: stageList
let groupLength = attGrouped.Length
if (activeCount + 1) <= groupLength
then
let selectCat,selectDetails = attGrouped.[activeCount + 1]
selectDetails
|> List.filter (fun x ->
stageList
|> List.exists (fun (x') ->
not(x' = (activeCount,x))))
|> (fun x ->
match x with
| [] -> ()
| head :: tail ->
stageList <- (activeCount, head) :: stageList
let currentCategory = activeCount + 1
calculateVariants attList currentCategory stageList
)
stageList // <-- error Unit expected
if .. then .. else should return the same type on both branches. If you omit else branch then compiler assuming that it returns unit. Add else branch returning list.
Edit:
Given your problem description, the easiest way would be something like this:
type NewProductAttributeInfo = {AttributeCategoryId: string; AttributeId: string}
let products = [ { AttributeCategoryId = "Size"; AttributeId = "S"};
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Color"; AttributeId = "White" };
{ AttributeCategoryId = "Color"; AttributeId = "Green" };
{ AttributeCategoryId = "Mat"; AttributeId = "Linen" };
{ AttributeCategoryId = "Mat"; AttributeId = "Cotton" };
{ AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
{ AttributeCategoryId = "Size"; AttributeId = "XL" } ]
let group list =
list
|> Set.ofList // Provides uniqueness of attribute combinations
|> Seq.groupBy (fun x -> x.AttributeCategoryId) // Grouping by CatId
|> List.ofSeq
let res = group products
Result:
val it : (string * seq<NewProductAttributeInfo>) list =
[("Color", seq [{AttributeCategoryId = "Color";
AttributeId = "Green";}; {AttributeCategoryId = "Color";
AttributeId "White";}]);
("Mat",
seq
[{AttributeCategoryId = "Mat";
AttributeId = "Cotton";}; {AttributeCategoryId = "Mat";
AttributeId = "Linen";};
{AttributeCategoryId = "Mat";
AttributeId = "Poliester";}]);
("Size", seq [{AttributeCategoryId = "Size";
AttributeId = "S";}; {AttributeCategoryId = "Size";
AttributeId = "XL";}])]
This is the solution that I came with. It works, but I'm sure it can be optimized quite a bit. I have a duplicate issue that is solved with the Set.ofList function externally after this code runs, which I'm still working on.
type NewProductAttributeInfo = {
AttributeId : string;
AttributeCategoryId : string
}
let rec private returnVariant (curIdx: int) (listLength: int)
(attList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list)
(curList: NewProductAttributeInfo list) =
match curList with
| x when x.Length = listLength -> curList
| x ->
let attTup =
attList
|> List.filter (fun x' ->
let idx1,att1,att2' = x'
idx1 >= curIdx && not(curList
|> List.exists (fun x'' ->
x'' = att2'))
)
let idx1,att1,att2 = attTup |> List.head
let newList = curList # [att2]
returnVariant idx1 newList.Length attList newList
let rec calculateVariants (attList: NewProductAttributeInfo list)
(currentList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list) =
// group attribute list by category id
let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
let (firstGroupCatId,firstGroupDetails) = attGrouped.[0]
match currentList with
| [] ->
let rawVariants = [for nxt in 0 .. (attGrouped.Length - 1) do
if nxt > 0
then
// begin iteration
for d in firstGroupDetails do
let _,det = attGrouped.[nxt]
for det' in det do
yield (nxt, d, det')
]
calculateVariants attList rawVariants
| x ->
let groupLength = x |> List.groupBy (fun (idx,d0,nxtD) -> idx)
|> List.length |> ((+)1)
let sortedGroup = x |> List.sortBy (fun (x,y,z) -> x)
if groupLength > 2
then // below is the block that generates the duplicates
[for att in sortedGroup do
for attCompare in sortedGroup do
let idx1,att1,att2 = att
let idx2,attC1,attC2 = attCompare
if idx2 > idx1 && att2 <> attC2
then
let idString =
returnVariant idx2 groupLength x [att1; att2; attC2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]
else
[
for att in sortedGroup do
let idx1,att1,att2 = att
let idString =
returnVariant idx1 groupLength x [att1; att2]
|> List.map (fun nl -> nl.AttributeId)
yield String.concat "," idString
]

How to deal with null values in F#

I am trying to fetch data(type:double) from MS access below. There are number of null values stored in A&B below. is there a way to change those null values to zeros?
let query sql w=
seq{
let conn = new OleDbConnection( #"Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=Portfolio.accdb;
Persist Security Info=False;" )
conn.Open()
let DAdapter = new OleDbDataAdapter(sql,conn)
let DTable = new DataSet()
let i= DAdapter.Fill(DTable)
let rowCol = DTable.Tables.[0].Rows
let rowCount = rowCol.Count
for i in 0 .. (rowCount - 1) do
yield w (rowCol.[i])
}
type Table1= {
A:double;
B:double}
let cf=query "SELECT * FROM T" (fun row ->
{
A=unbox(row.["A"]);
B=unbox(row.["B"]);})
Define a function
let toFloat = function
| null -> 0.0
| obj -> unbox obj
And then use it as follows
let cf = query "SELECT * FROM T" (fun row ->
{ A = toFloat row.["A"]
B = toFloat row.["B"] } )
Maybe, your columns in DB have different type (for example, int and double). Or try check return value with DBNull type:
let toDouble x =
if System.Convert.IsDBNull(x) then 0.0
else System.Double.Parse(x.ToString())
To check I create that table:
And with your code:
open System.Data
open System.Data.OleDb
let toDouble x =
if System.Convert.IsDBNull(x) then 0.0
else System.Double.Parse(x.ToString())
let query sql w=
seq{
let conn = new OleDbConnection( #"Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=F:/Portfolio.accdb;Persist Security Info=False;" )
conn.Open()
let DAdapter = new OleDbDataAdapter(sql,conn)
let DTable = new DataSet()
let i = DAdapter.Fill(DTable)
let rowCol = DTable.Tables.[0].Rows
let rowCount = rowCol.Count
for i in 0 .. (rowCount - 1) do
yield w (rowCol.[i])
conn.Close()
}
type Table1= { A:double; B:double }
let cf = query "SELECT * FROM T" (fun row -> { A = toDouble row.["A"]; B = toDouble row.["B"] } )
cf |> Seq.iter(fun x -> printfn "%A" x)
Result:
{A = 1.0;
B = 2.2;}
{A = 3.0;
B = 0.0;}
{A = 4.0;
B = 0.0;}

Is there a way to go from Gen<a> to Gen<a list> while specifying a bound?

module BirthdayLibrary =
type Birthday =
{ day :int
month :int }
module DataGen =
let birthdayGenerator (months:option<list<int>>) =
let monthGen = match months with
| Some m -> Gen.elements m
| None -> FsCheck.Gen.choose(1,12)
(fun m d -> { day = d
month = m}:BirthdayLibrary.Birthday)
<!> monthGen
<*> FsCheck.Gen.choose(1,28)
//I want this to have the signature Gen<BirthdayLibrary.Birthday list>
let oneForEveryMonthGen =
[ for m in [1 .. 12] ->
(fun mo -> birthdayGenerator (Some [mo]))]
Lets say I have something like the above. I have a birthday generator with the return type of Gen<BirthdayLibrary.Birthday>. You can optionally specify the month. What is the best way to go from a Gen<a> to a Gen<a list> WHILE specifying some constraint to item in the list's gen? I can think of ways to get <Gen<a> list>, which I don't think would work for composing a parent object that should contain a <list a> (which is the basis for this example). I can also think of a way to do Gen<Gen<a> list>, which I had composition problems with as well as all values in the list being the same. I saw Gen.arrayOf bit I can't think of how to get it to work with values for each item in the array.
module BirthdayLibrary =
type Birthday =
{ day :int
month :int }
module DataGen =
let birthdayGenerator (months:option<list<int>>) =
let monthGen = match months with
| Some m -> Gen.elements m
| None -> FsCheck.Gen.choose(1,12)
(fun m d -> { day = d
month = m}:BirthdayLibrary.Birthday)
<!> monthGen
<*> FsCheck.Gen.choose(1,28)
//I want this to have the signature Gen<BirthdayLibrary.Birthday list>
let oneForEveryMonthGen =
[ for m in [1 .. 12] -> birthdayGenerator (Some [m])]
|> Gen.sequence
let sample = DataGen.oneForEveryMonthGen |> Gen.sample 10 1
gives me:
val sample : BirthdayLibrary.Birthday list list = [[{day = 7;
month = 1;}; {day = 15;
month = 2;}; {day = 13;
month = 3;}; {day = 14;
month = 4;}; {day = 28;
month = 5;};
{day = 9;
month = 6;}; {day = 28;
month = 7;}; {day = 15;
month = 8;}; {day = 6;
month = 9;}; {day = 21;
month = 10;};
{day = 12;
month = 11;}; {day = 27;
month = 12;}]]

Resources