Good material to read on Map in F# - f#

Please help with the following exercise:
what are good source of material can I read to understand how to use Map in F#?
I tried to understand how to use Map.
I tried:
let holidays =
Map.empty.
Add("Christmas", "Dec. 25");;
It works then I tried:
let holidays =
Map.empty.
Add("Christmas", "Dec. 25")
Add("Halloween", "Oct. 31");;
and it gave an error
I had to put a dot after the first Add statement like this:
let holidays =
Map.empty.
Add("Christmas", "Dec. 25").
Add("Halloween", "Oct. 31");;
I'm wondering why is that.
So then that means
let holidays =
Map.empty;;
is how we create a new map. But I want a map of type Map (Team, Points).
So I tried:
let exmaple =
Map.empty.
Add ("Superman", Points 8);;
val exmaple : Map<string,Points> = map [("Superman", Points 8)]
But this is of type map(string, Points) not map(Team, Points).
Also, How do I write "<" after map on stackoverflow so that things follow "<" do appear(that is why i use map() instead of map<>)?
Please help.

Three ways of creating an empty Map<Team,Points> are:
let holidays : Map<Team, Points> = Map.empty
let holidays' = Map.empty : Map<Team, Points> // this also works
let holidays'' = Map.empty // even this works if followed by usage
You can add elements with either .Add(t, p) or with Map.add t p:
let twoAdded = holidays .Add("Superman", Points 6) .Add("Batman", Points 5)
let twoAdded' = holidays |> Map.add "Superman" (Points 6) |> Map.add "Batman" (Points 5)
The second way is the functional way.
Your type Team and string are the same because:
type Team = string
is just an alias not a new type.

Related

F# Convert CsvFile to Json object array

Trying to learn F# and got stuck when trying to find a better approach of converting a csv file to a json array where each row + header is a json object in that array.
After some trial and error I finally caved and went for an ugly approach with mutable list and map. Are there any better ways this can be implemented?
let csvFileToJsonList (csvFile: FSharp.Data.CsvFile) =
let mutable tempList = List.empty<Map<string,string>>
let heads =
match csvFile.Headers with
| Some h -> h
| None -> [|"Missing"|] // what to do here?
let nbrOfColumns = csvFile.NumberOfColumns
for row in csvFile.Rows do
let columns = row.Columns
let mutable tempMap = Map.empty<string,string>
for i = 0 to nbrOfColumns-1 do
tempMap <- tempMap.Add(heads.[i], columns.[i])
tempList <- tempMap :: tempList
System.Text.Json.JsonSerializer.Serialize(tempList)
This outputs the following which is the goal:
[
{
"Header1": "Row1Val1",
"Header2": "Row1Val2",
"Header3": "Row1Val3",
"Header4": "Row1Val4",
"Header5": "Row1Val5"
},
{
"Header1": "Row2Val1",
"Header2": "Row2Val2",
"Header3": "Row2Val3",
"Header4": "Row2Val4",
"Header5": "Row2Val5"
}
]
This is about as simple as I could make it, although a longer version might be more readable for you:
let csvFileToJsonList (csvFile: FSharp.Data.CsvFile) =
let heads = csvFile.Headers |> Option.defaultValue [||]
csvFile.Rows
|> Seq.map (fun row -> Seq.zip heads row.Columns |> Map)
|> System.Text.Json.JsonSerializer.Serialize
This produces the output in the original order, which I'm assuming is preferable (your solution reverses the order).
This also assumes some headers exist, otherwise the output will be empty objects.
Description: For each row use Seq.zip to produce a sequence of header-value tuples. Pass that to the Map constructor to create a map, providing a sequence of maps, which can be serialized.
Note that using dict instead of Map might be a bit faster.
You also could use CsvProvider to create a typed object (Row)
open FSharp.Data
type Persons =
CsvProvider<"David,Raab,19.02.1983",
Schema="First (string), Last (string), BirthDay(string)",
HasHeaders=true>
let parseCsv (reader:System.IO.TextReader) = [
let data = Persons.Load reader
for row in data.Rows do
Map [
("First", row.First)
("Last", row.Last)
("Birthday", row.BirthDay)
]
]
Returning a List of a map instead of Json, but i guess you will know how to change it to Serialze the data.

Names bound to both the decomposed discriminated union components AND an original composed value

I can't find documentation on this. I need to decompose a Discriminated Union value into some of its components but I also need to use the item as a whole within the body of a function as well.
I can do let matcher = function MyDU(_,b,_) -> b or let extractor MyDU(_,b,_) = b but what if I also need the reference to the MyDU value for something like ... -> RedundantWrapper(myDU, b)
I've tried:
let extractor myDU =
let MyDU(_,b,_) = myDU
RedundantWrapper(myDU, b)
but I don't think that is the right syntax. Perhaps I can do this some longer way, but it seems like there would be a short way.
Thanks!
Like this:
let (Some(x) as o) = Some 1
// val o : int option = Some 1
// val x : int = 1
Also, I just realized that this short-hand is allowed which I think everyone here will agree is PRETTY AWESOME.
let extractor (myDU & MyDU(_,b,_)) =
RedundantWrapper(myDU, b)

Type annotation for using a F# TypeProvider type e.g. FSharp.Data.JsonProvider<...>.DomainTypes.Url

I'm using the FSharp.Data.JsonProvider to read Twitter Tweets.
Playing with this sample code
https://github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
I want to expand the urls in the tweet with
let expandUrl (txt:string) (url:Search.DomainTypes<...>.DomainTypes.Url) =
txt.Replace( url.Url, url.ExpandedUrl )
This results in Error:
Lookup on object of indeterminate type based on information prior to this program point.
A type annotation may be needed prior to this program point to constrain the type of the object.
My problem is how to define the TypeProvider Type for url in the expandUrl function above?
The type inferance shows me this
val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
but this is not accepted in the type declaration. I assume "<...>" is not F# synatx.
How to do a type annotation for using a TypeProvider type e.g. FSharp.Data.JsonProvider<...>.DomainTypes.Url ?
Here is the complete code snippet:
open TwitterAPI // github.com/tpetricek/Documents/tree/master/Samples/Twitter.API
let twitter = TwitterAPI.TwitterContext( _consumerKey, _consumerSecret, _accessToken, _accessTokenSecret )
let query = "water"
let ts = Twitter.Search.Tweets(twitter, Utils.urlEncode query, count=100)
let ret =
[ for x in ts.Statuses do
// val urls : FSharp.Data.JsonProvider<...>.DomainTypes.Url []
let urls = x.Entities.Urls
// fully declarated to help the type inference at expandUrl
let replace (txt:string) (oldValue:string) (newValue:string) =
txt.Replace( oldValue, newValue)
// Error:
// Lookup on object of indeterminate type based on information prior to this program point.
// A type annotation may be needed prior to this program point to constrain the type of the object.
// This may allow the lookup to be resolved.
let expandUrl (txt:string) (url:FSharp.Data.JsonProvider<_>.DomainTypes.Url) =
replace txt url.Url url.ExpandedUrl
let textWithExpandedUrls = Array.fold expandUrl x.Text urls
yield textWithExpandedUrls
]
When you call Twitter.Search.Tweets (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L284), the return type of that is one of the domain types of TwitterTypes.SearchTweets, which is a type alias for JsonProvider<"references\\search_tweets.json"> (https://github.com/tpetricek/Documents/blob/master/Samples/Twitter.API/Twitter.fs#L183).
Although in the tooltip it shows up as JsonProvider<...>.DomainTypes.Url, you'll have to use the type alias TwitterTypes.SearchTweets.DomainTypes.Url
I had a similar problem trying to figure out how to use the FSharp.Data HtmlProvider.
I am using Wikipedia to get information about USA presidents. The HtmlProvider does a great job of discovering the various tables in that webpage, but I wanted to extract the logic for processing a row of "president data" into a separate function called processRow.
And the problem was trying to work out what the type of such a row is for processRow's parameter row. The following code does the trick:
#load "Scripts\load-references.fsx"
open FSharp.Data
let presidents = new HtmlProvider<"https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States">()
let ps = presidents.Tables.``List of presidents``
ps.Headers |> Option.map (fun hs -> for h in hs do printf "%s " h)
printfn ""
type Presidents = ``HtmlProvider,Sample="https://en.wikipedia.org/wiki/List_of_Presidents_of_the_United_States"``.ListOfPresidents
let processRow (row:Presidents.Row) =
printfn "%d %s" row.``№`` row.President2
ps.Rows |> Seq.iter processRow
I did not type in the long type alias for Presidents, I used Visual Studio auto-completion by guessing that the type for List of presidents would be discoverable from something starting with Html, and it was, complete with the four single back quotes.

Project new values from existing value

I'm writing my very first F# program, the aim being simply to learn F#.
What I want to is provide a list of dates, and attributes (e.g.DayOfWeek, DayOfMonth) of those dates. I have managed to provide the list of dates and I know that the .net Framework gives me everything I need to extract all the attributes, I just can't figure out how to add the attribute as new columns in my list.
Here's what I have so far:
type Span = Span of TimeSpan with
static member (+) (d:DateTime, Span wrapper) = d + wrapper //this is defining the + operator
static member Zero = Span(new TimeSpan(0L))
type Dates() =
let a = DateTime.Parse("01/12/2013")
let b =DateTime.Parse("02/12/2013")
let ts = TimeSpan.FromDays(1.0)
member this.Get() = [a .. Span(ts) .. b]
let mydates = new Dates()
mydates.Get()
When I run that code I get a list of DateTime values, with 2 records in the list. I can now do something like this:
mydates.Get() |> List.map (fun x -> x.DayOfWeek);;
which returns:
val it : DayOfWeek list = [Sunday; Monday]
or
mydates.Get() |> List.map (fun x -> x.DayOfYear);;
which returns:
val it : int list = [335; 336]
That's all great, however what I would like to do is project a list that has 2 "columns" (if columns is the right word) so that my output is (something like):
val it : int list = [(Sunday,335); (Monday,336)]
I hope that explains what I'm after.
thanks
Jamie
For your example, the solution is simple, make the map return a tuple like so
mydates.Get() |> List.map (fun x -> x.DayOfWeek,x.DayOfYear);;

looping through F# record like Javascript object

In javascript, I can access every property of an object with a simple for loop as follows
var myObj = {x:1, y:2};
var i, sum=0;
for(i in myObj) sum = sum + myObj[i];
I am wondering if I can do similar thing with F#.
type MyObj = {x:int; y:int}
let myObj = {x=1; y=2}
let allValues:seq<int> = allPropertyValuesIn myObj //How do I implement allPropertyValuesIn
let sum = allValues |> Seq.fold (+) 0
Thank you for your input
Edit to clarify why I want to do such thing
I am working on an XML file generator. The input is rows read from Database, and the xsd is predefined.
Lets say I have a "Product" Element needs to be generated and depending on the business rule, there could be 200 Children element under product some are required, some are optional. Following the advise from this excellent blog, I have had my first (very rough) design for product record:
1. type Product{ Price:Money; Name:Name; FactoryLocation:Address option ... }
2. let product = {Price = Money(1.5); Name = Name ("Joe Tooth Paste"); ... }
3. let child1 = createEl ("Price", product.Price)
..
203. let allChildren = child1
::child2
::child3
..
::[]
404. let prodctEl = createElWithCildren ("Product", allChildren)
This is very tedious and un-succinct. There HAS to be a better way to do such thing in F#. I am not very kin on the reflection idea either.
Are there any other approaches or I am just doing it wrong?
Try this:
open Microsoft.FSharp.Reflection
type MyObj = {x:int; y:int}
let myObj = {x=1; y=2}
let allValues = FSharpType.GetRecordFields (myObj.GetType())
let sum =
allValues
|> Seq.fold
(fun s t -> s + int(t.GetValue(myObj).ToString()))
0
printfn "%d" sum
However, as John Palmer admonishes, there are not very many good reasons for doing something like this.

Resources