I have SQL table for hierarchical data:
Maximum level of hierarchy is 5.
Lines #4 and #5 are children of line #1, for example.
I heed to have query expression to get child records by given one. Now I have this pattern matching:
let private queryForChild (db: dbml.MobileDataContext) id1 id2 id3 id4 id5 et =
match (id1, id2, id3, id4, id5) with
| _, "", "", "", "" -> query {
for rows in db.ItemType do
where (rows.Id1 = id1 && rows.Id2 <> "" && rows.Id3 = "" && rows.Id4 = "" && rows.Id5 = "" && rows.EntityType = et)
select rows
}
| _, _, "", "", "" -> query {
for rows in db.ItemType do
where (rows.Id1 = id1 && rows.Id2 = id2 && rows.Id3 <> "" && rows.Id4 = "" && rows.Id5 = "" && rows.EntityType = et)
select rows
}
| _, _, _, "", "" -> query {
for rows in db.ItemType do
where (rows.Id1 = id1 && rows.Id2 = id2 && rows.Id3 = id3 && rows.Id4 <> "" && rows.Id5 = "" && rows.EntityType = et)
select rows
}
| _, _, _, _, "" -> query {
for rows in db.ItemType do
where (rows.Id1 = id1 && rows.Id2 = id2 && rows.Id3 = id3 && rows.Id4 = id4 && rows.Id5 <> "" && rows.EntityType = et)
select rows
}
| _, _, _, _, _ -> query {
for rows in db.ItemType do
where (rows.Id1 = "-1")
select rows
}
I don't like it and wondering is there any way to rewrite it using boolean operators to avoid pattern matching ?
It is indeed not possible to use custom functions in a query computation, because the inside of the query computation gets quoted (as an F# Quotation) and then (ultimately) translated to SQL, and custom functions can't be thus translated.
However, unlike C#, F# does offer a code reuse facility within code quotations - it's called "splicing".
Consider an example:
let q = query { for x in listOfInts do yield x + 42 }
> q.Expression
val it : Expression = [1; 2; 3; ... ].Select(_arg1 => (_arg1 + 42))
Let's say I really don't like that + 42 over there, I'd like to abstract it away. Well, I can do it like this:
let add42 = <# fun i -> i + 42 #>
let q = query { for x in listOfInts do yield (%add42) x }
If we now examine q.Expression, we'll find that it's identical to the previous version:
> q.Expression
val it : Expression = [1; 2; 3; ... ].Select(_arg1 => (_arg1 + 42))
Here's what's happened here. add42 is a code quotation that contains a function that adds 42 to its argument. The %add42 expression "inserts" (aka "splices") that quotation in the middle of the larger quotation, resulting in an expression like this:
let q = query { for x in listOfInts do yield (fun i -> i + 42) x }
This expression then got simplified during translation from F# code quotation to System.Linq.Expressions.Expression, resulting in an expression identical to the first version.
The final piece to add: spliced quotations don't have to be "constant", they can be produced by functions too. These functions get evaluated during construction of the overall quotation, and their results then get spliced. For example, I could redefine the above code like this:
let add a = <# fun x -> x + a #>
let q2 = query { for x in list do yield (% add 42) x }
Now add is a function that takes 42 as argument and produces a code quotation that contains another function. Phew!
And now we can apply all that to your case: make yourself a function that will take idx as argument and produce a quotation of a function that, after splicing, would be applied to row.idx:
// NOTE: I'm not sure if this logic is correct. You'll have to verify it.
//
// For the i-th ID:
// * if all previous IDs are non-empty,
// but the i-th ID itself is empty,
// then the condition should check for i-th ID being non-empty.
// This means "query rows of i-th level".
// * if all previous IDs are non-empty,
// and the i-th ID itself is non-empty,
// then the condition should check for i-th ID being equal to
// This means "query rows of j-th level", where j > i
// * Otherwise, the condition should check for
// the i-th ID being empty.
// This means "query rows of j-th level", where j < i
let compare prevIds thisId =
if List.all ((<>) "") prevIds
then if thisId = ""
then <# fun id -> id <> "" #>
else <# fun id -> id = thisId #>
else <# fun id -> id = "" #>
let private queryForChild (db: dbml.MobileDataContext) id1 id2 id3 id4 id5 et =
query {
for rows in db.ItemType do
where (
(% compare [] id1) rows.Id1 &&
(% compare [id1] id2) rows.Id2 &&
(% compare [id1; id2] id3) rows.Id3
(% compare [id1; id2; id3] id4) rows.Id4
(% compare [id1; id2; id3; id4] id5) rows.Id5 &&
rows.EntityType = et )
select rows
}
Also note that the way you constructed your function, its behavior is not well defined for inputs with "holes" - i.e. id1="x", id2="", id3="y" - did you mean to query second or fourth level in this case? I would recommend a better data structure that excludes nonsensical inputs.
Perhaps, you can use something like this:
let equalOrNotEmpty a b =
match a with
| "" -> b <> ""
| a -> a = b
and usage would be
let private queryForChild (db:dbml.MobileDataContext) id1 id2 id3 id4 id5 =
query {
for rows in db.ItemType do
where (equalOrNotEmpty rows.Id1 id1
&& equalOrNotEmpty rows.Id2 id2
&& equalOrNotEmpty rows.Id3 id3
&& equalOrNotEmpty rows.Id4 id4
&& equalOrNotEmpty rows.Id5 id5
&& rows.EntityType = et)
select rows
}
I have a situation with 3 use cases for returning data from an mnesia table
1. return all values of a table so I use a foldl,
2. return 1 row so I use read
3. return a variable number of records based on criteria so I use select.
I would like to use the same code to manage the results, but the select returns a different data structure. I am hoping someone can help me restructure my select to return the same as the others.
below is sample code of the issue and the results. The issue is the select does not return the record name for the table as does read and foldl.
-module(testselect2).
-export([runtest/0]).
-record(record_a, {b, c, d}).
-record(record_b, {record_a, e}).
-record(record_c, {record_b, f, intval}).
runtest() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(record_c, [{attributes, record_info(fields, record_c)}]),
A1 = #record_a{b = "R1", c = "T1", d = "C1"},
B1 = #record_b{record_a = A1, e = "E1"},
C1 = #record_c{record_b = B1, f = "F1", intval = 100},
A2 = #record_a{b = "R2", c = "T2", d = "C2"},
B2 = #record_b{record_a = A2, e = "E2"},
C2 = #record_c{record_b = B2, f = "F2", intval = 200},
A3 = #record_a{b = "R3", c = "T3", d = "C3"},
B3 = #record_b{record_a = A3, e = "E3"},
C3 = #record_c{record_b = B3, f = "F3", intval = 300},
{atomic, Rw} = mnesia:transaction(
fun () ->
mnesia:write(C1),
mnesia:write(C2),
mnesia:write(C3)
end),
io:fwrite("Result write = ~w~n", [Rw]),
{atomic, Rr} = mnesia:transaction(
fun () ->
mnesia:read({record_c, B1})
end),
io:fwrite("Result read = ~w~n", [Rr]),
{atomic, Rf} =
mnesia:transaction(fun () ->
mnesia:foldl(fun (Rec, Acc) -> [Rec | Acc] end,
[],
record_c)
end),
io:fwrite("Result foldl = ~w~n", [Rf]),
MatchHead = #record_c{record_b='$1', f='$2', intval='$3'},
Guard = {'>', '$3', 100},
Result = {{'$1', '$2', '$3'}},
{atomic, Rs} = mnesia:transaction(
fun () ->
mnesia:select(record_c, [{MatchHead, [Guard], [Result]}])
end),
io:fwrite("Result select = ~w~n", [Rs]).
=====
RESULTS
44> testselect2:runtest().
Result write = ok
Result read = [{record_c,{record_b,{record_a,[82,49],[84,49],[67,49]},[69,49]},[70,49],100}]
Result foldl = [{record_c,{record_b,{record_a,[82,49],[84,49],[67,49]},[69,49]},[70,49],100},{record_c,{record_b,{record_a,[82,51],[84,51],[67,51]},[69,51]},[70,51],300},{record_c,{record_b,{record_a,[82,50],[84,50],[67,50]},[69,50]},[70,50],200}]
Result select = [{{record_b,{record_a,[82,51],[84,51],[67,51]},[69,51]},[70,51],300},{{record_b,{record_a,[82,50],[84,50],[67,50]},[69,50]},[70,50],200}]
ok
As you can see above read and foldl records start with {record_c,{... where select is missing the record_c and just has {{...
I have been unable to find a way to get select to return the same structure so my processing code can work for all 3 use cases. Any suggestions would be greatly appreciated.
I'm no mnesia expert, but I know when you use an ETS match expression, you determine what the result looks like. You use Result = {{'$1', '$2', '$3'}} to create your result terms, which makes them come out as three-tuples in a one-tuple, as we see in your output. Per ets:select/1, you want to use the special variable '$_' to return the whole matched object, so this should work in place of your Result = ... line:
Result = '$_',
Consider
type alias Rec = { a: Int, b: Int, c:Int }
updateRec r aVal = { r|a = aVal }
updateRec2 r aVal bVal = { r|a = aVal, b= bVal }
updateRec3 r aVal bVal cVal = ...
How to generalize updateRec and updateRec2... into one function?
Here's a better way of doing the same thing:
updateA : Int -> Rec -> Rec
updateA x rec = { rec | a = x }
-- Similarly for b, c
Now you can do the following, supposing you already have a value rec : Rec you want to update:
myUpdatedRec : Rec
myUpdatedRec =
rec
|> updateA 7
|> updateB 19
You can now update an arbitrary number of fields by stringing together |> updateX ....
You want to write a function that has a variable number of differently-typed parameters. That's common in dynamically typed languages (Javascript, Python, Ruby) but usually not allowed in typed languages. Elm doesn't allow it.
You can emulate a variable number of differently-typed parameterswith the Maybe type, understanding Nothing as "missing argument":
updateRec : Rec -> Maybe Int -> Maybe Int -> Maybe Int -> Rec
updateRec r a b c =
{ r
| a = a |> Maybe.withDefault r.a
, b = b |> Maybe.withDefault r.b
, c = c |> Maybe.withDefault r.c
}
If the record fields are all of the same type (here Int), you could accept a List Int instead:
updateRec : Rec -> List Int -> Rec
updateRec r fields =
case fields of
[a] -> { r | a = a }
[a,b] -> { r | a = a, b = b }
[a,b,c] -> { r | a = a, b = b, c = c }
_ -> r
I don't like this solution because you it'll fail silently if you accidentally supply a list with 0 or 4+ elements. If this function is helpful to you anyway, perhaps it would be better to use List Int instead of Rec in the first place.
Elm's record update syntax seems to be exactly what you are looking for. You "pass in" the record that you want updated, r below, and you can return a record with whatever fields you want changed without having to specify every field:
ex1 = { r|a = 1 }
ex2 = { r|b = 2, c = 3 }
You don't need to create a set of additional functions for updating only certain fields at certain times, because Elm's record update syntax is that generalized function.
I am new to F# and have been messing around with records and changing them. I am trying to apply my own function with out using map to my list. This is what i have so far. I am just wondering if my approach for how to write a mapping without using the map function the correct way of thinking about it.
module RecordTypes =
// creation of simple record
// immutable by default - key word mutable allows that to change
type Student =
{
Name : string
mutable age : int
mutable major : string
}
// setting up a few records with student information
// studentFive.age <- studentFive.age + 2 ; example of how to change mutable variable
let studentOne = { Name = "bob" ; age = 20 ; major = "spanish" }
let studentTwo= { Name = "sally" ; age = 18 ; major = "english" }
let studentThree = { Name = "frank" ; age = 22 ; major = "history" }
let studentFour = { Name = "lisa" ; age = 19 ; major = "math" }
let studentFive = { Name = "john" ; age = 17 ; major = "philosophy" }
// placing the records into a lits
let studentList = [studentOne; studentTwo; studentThree ;studentFour; studentFive]
// placing the records into a lits
let studentList = [studentOne; studentTwo; studentThree ;studentFour; studentFive]
// itterate through a list and printing each records
printf "the unsorted list of students: \n"
studentList |> List.iter (fun s-> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
// a sort of the records based on the name, can be sorted by other aspects in the records
let sortStudents alist =
alist
|> List.sortBy (function student -> student.age)
let rec selectionSort = function
| [] -> [] //if the list is empty it will return an empty list
| l -> let min = List.min l in // otherwise set a min variable and use the min function to find the smallest item in a list
let rest = List.filter (fun i -> i <> min) l in // set a variable to hold the rest of the list using filter
// Returns a new collection containing only the elements of the collection for which the given predicate returns true
// fun sets up a lambda expression that if ( i -> i <> (not equal boolean) min) if i(the record is not the min put it into a list)
let sortedList = selectionSort rest in // sort the rest of the list that isnt the min
min :: sortedList // :: is an operator that creates a list, left elem appended to right side
let unsortedList = studentList
let sortedList = selectionSort unsortedList
printfn "sorted list based on first name:\n"
sortedList |> List.iter(fun s -> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
here is where i tried to create my own map with function foo
let foo x = x + 1
let applyOnEachElement (list : Student list) (someFunction) =
list |> List.iter(fun s -> someFunction s.age)
//let agedStudents = applyOnEachElement studentList foo
printf " the students before function is applied to each: \n"
sortedList |> List.iter(fun s -> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
printf " the student after function is applied to each: \n"
agedStudents |> List.iter(fun s -> printf "Name: %s, Age: %d, Major: %s\n" s.Name s.age s.major)
In the last comment, the OP mentions his almost complete solution. With a bit of added formatting and a forgotten match construct, it looks as follows:
let rec applyOnEachElement2 (list: Student list) (f) =
match list with
| [] -> []
| hd :: tl -> hd::applyOnEachElement2 f tl
This is quite close to the correct implementation of map function! There are only two issues:
when calling applyOnEachElement2 recursively, you switched the parameters
the f parameter is passed recursively but never actually used for anything
To fix this, all you need is to switch the order of parameters (I'll do this on the function arguments to get the parameters in the same order as standard map) and call the f function on hd on the last line (so that the function returns a list of transformed elements):
let rec applyOnEachElement2 f (list: Student list) =
match list with
| [] -> []
| hd :: tl -> (f hd)::applyOnEachElement2 f tl
You can also make it generic by dropping the type annotation, which gives you a function with the same type signature as the built in List.map:
let rec applyOnEachElement2 f list =
match list with
| [] -> []
| hd :: tl -> (f hd)::applyOnEachElement2 f tl
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;}