F# Finding a Set in a Map - f#

So I have a database containing cars with an associated number and description. That database looks like this:
type Database = Map<CarNo, CarDesc>
type CarNo = int
type CarDesc = Name * Price
type Name = string
type Price = int
If I want to get the sum of the price of all the cars, based on the numbers, how would I make that function? I'm a little stuck on how to begin.
Any hints appreciated!

I would imagine that a function like this one would sum the prices of all the cars in your map. But contra the question title, there's no sets involved here.
let sumPrices (db:Database) =
db |> Map.fold (fun acc _ (_, price) -> acc + float price) 0.0

db |> Seq.sumBy (fun (KeyValue (_, (_, price))) -> price)

Related

f# how to access the data from another function?

I am having trouble with accessing a value from another function in f sharp. Below is my code, I am taking the user input a student name and 3 exam scores and calculating their averages and saving that in the variable "let average" of the InputStudents function. Now I am trying to access that value in another function PringtAverages as shown below, but it would not compile. I need help to fix this issue. Appreciate if you can help. Thank you.
let rec InputStudents students =
let line = System.Console.ReadLine()
match line with
| "#" -> (List.rev students)
| _ ->
let data = line.Split ' '
//Getting the student name at data.[0], and getting the 3 exam
//scores in data.[1]..[3].
let student = (data.[0], [int(data.[1]); int(data.[2]); int(data.[3])])
//calculating the average by for the 3 exam scores
let average = (data.[0], [(float(data.[1]) + float(data.[2]) + float(data.[3]))/3.0])
InputStudents (student:: students)
//Function below I am trying to get the above-calculated averages and print
let rec PrintAverages L =
match L with
| [] -> ()
| e::rest ->
let avg = InputStudents[]
printfn "%A: %A" "netid" avg.average //Here I am trying to print
//the average calculated in the function
//InputStudents[] above
PrintAverages rest
That is not possible.
You cannot access internal calculations from another function. What you need to do is return the values that you need to use outside.
In your case your function InputStudents has the following signature:
(string * int list) list -> (string * int list) list
Which means it returns a list with each student name and notes. The average is calculated but then is lost, because it is not used anywhere. If you want to be able to print it in another function you need to include it as part of the return value:
...
let name = data.[0]
let scores = [float data.[1]
float data.[2]
float data.[3] ]
//calculating the average by for the 3 exam scores
let average = List.average scores
InputStudents ((name, scores, average) :: students)
now the signature is this:
(string * float list * float) list -> (string * float list * float) list
indicating it returns a tuple for each student that includes name, notes and average.
Now lets address the PrintAverages function.
The function has a problem: it calls InputStudents and it calls itself recursively. What you want to do is call first InputStudents and then pass the result to PrintAverages:
InputStudents [] |> PrintAverages
Also in your match statement you can unpack the tuple that you are receiving. Right now you have e::rest which gives you one element and the rest of the list. That element would be of type string * float list * float which you can unpack like this:
let name, notes, average = e
or directly in the match statement:
match L with
| [] -> ()
| (name, notes, average) :: rest ->

Get the count of distinct values from a Sequence in F#

I have a sequence of Country names in F#. I want to get how many of each distinct country entries do I have in the sequence.
The countBy examples in Microsoft docs and MSDN use if and else to get the Keys, but since I have ~240 distinct entries, I guess that I don't need to make an elif sentence for each entry, right?
So, is there an option to use another sequence to get the keys for the countBy?
#load "packages/FsLab/FsLab.fsx"
open FSharp.Data
open System
type City = JsonProvider<"city.list.json",SampleIsList=true>
let cities = City.GetSamples()
let Countries = seq { for city in cities do yield city.Country.ToString() } |> Seq.sort
let DistinctCountries = Countries |> Seq.distinct
//Something like this
let Count = Seq.countBy DistinctCountries Countries
Anyone interested in my city.list.json
Update
My input sequence is something like this (with a lot more of entries) with each code repeated as many cities for that country are in the original list:
{ "AR","AR","AR","MX","MX" }
As a result I expect:
{("AR", 3),("MX", 2),...}
Countries |> Seq.countBy id
id is the identity function fun x -> x. Use this because the "key" here is the sequence item itself.
You can group the countries and then count the number of entries in each group:
let countsByCountry =
Countries
|> Seq.groupBy id
|> Seq.map (fun (c, cs) -> c, Seq.length cs)
This combination is also implemented as a single function, countBy:
let countsByCountry = Countries |> Seq.countBy id
So, is there an option to use another sequence to get the keys for the countBy?
You do not need to get the keys from somewhere, the function passed to Seq.countBy generates the keys. You should be able to get away with this:
let count =
cities
|> Seq.countBy (fun c -> c.Country.ToString())

Does record type with key exist in F#

In OPL (Optimization Programming Language), we have a data structure name tuple. OPL tuple correspond to Record in F#. Here is how it is defined:
tuple Point {
int x;
int y;
};
Like in F#, we can access field by using dot notation
int x = p.x;
We can group tuples in a Set:
{Point} points = {<1,2>, <2,3>};
A difference is that like in database systems, tuple structures can be associated with keys. Tuple keys enable to access data organized in tuples using a set of unique identifiers. In the following example, the nurse tuple is declared with the key name of type string.
tuple nurse {
key string name;
int seniority;
int qualification;
int payRate;
}
{ nurse } nurses = …;
The nice thing about key, is that we can initialize an array this way
int NumberOfChild [n in nurses] = 0;
and accessing a value by using only the key:
NumberOfChild[<"Isabelle">]=20;
leaving out the fields with no keys. This is equivalent to:
NumberOfChild[<"Isabelle",3,1,16>]=20;
Also, using key means that there will be no two tuples with the same key. Like primary key in database.
Question is: Does some type like this exist in F#? Record with key?
My goal: I would like to define a node structure with many attribute. And load a graph structure by only giving the key of the node and not the entire Record since I would load the graph from a database.
type Node = {
nodeKey : int;
nodeName : string;
nodeAttribute1 : string;
nodeAttribute2 : string }
let Graph = [
(1, 2);
(1, 3);
(2, 4);
(3, 4) ]
Where the int in the graph tuple represent nodeKey.
I would like to do operation using the graph but accessing the node information using the key only.
OPL Grammar
No, there's no such language-level concept. All record fields are created equal, so to speak.
That doesn't preclude you from:
synthesizing a key for a record based on one or more field values,
using such a key as a key in a Map that would hold your records or any additional values.
So you can have something like this:
type Nurse = { name: string; seniority: int; qualification: int; payRate: int }
let nurses = [ { name = "Isabelle"; seniority = 3; qualification = 1; payRate = 16 } ]
let numberOfChildren =
[ "Isabelle", 20 ]
|> Map.ofSeq
let nursesWithNumberOfChildren =
[ for nurse in nurses do
match numberOfChildren |> Map.tryFind nurse.name with
| Some children -> yield nurse, children
| None -> yield nurse, 0 ]
Using similar approach you can separate your graph and node data - store only keys in the graph and maintain a mapping that goes from keys to full node records.
//If I read data from a database, I would receive the data in the following form:
type XYZ = {X:int;
Y:string;
Z:float}
let recordsXYZ = [{X=1;Y="A";Z=1.0};{X=2;Y="b";Z=1.0};{X=3;Y="A";Z=1.0}]
//I can create a map this way
let mapXYZ1=recordsXYZ|>Seq.groupBy (fun a ->a.X)|>Map.ofSeq
//But I don't want a Map<int,seq<XYZ>>
//This is what I want
let mapXYZ2=recordsXYZ|>Seq.map (fun a -> (a.X,{X=a.X;Y=a.Y;Z=a.Z}))|>Map.ofSeq
//Or maybe this is cleaner but this need to define another type
type YZ = {Y:string;
Z:float}
let mapXYZ3=recordsXYZ|>Seq.map (fun a -> (a.X,{Y=a.Y;Z=a.Z}))|>Map.ofSeq
If I understand correctly, your best bet is simply a cleaner alternative to Seq.groupBy for your purposes. Here is the core of it, in one line:
let inline project projection value = projection value, value
Given a simple helper function, not specific to XYZ
let projectToMap projection values = values |> Seq.map (project projection) |> Map.ofSeq
it becomes trivial to cleanly create maps of XYZ from any "key":
let mappedByX = xyzs |> projectToMap (fun { X=x } -> x) // Map<int, XYZ>
let mappedByY = xyzs |> projectToMap (fun { Y=y } -> y) // Map<string, XYZ>
let mappedByZY = xyzs |> projectToMap (fun { Y=y; Z=z } -> z, y) // Map<float*string, XYZ>
Online Demo

Functional way to add to Lists that are Class-Members

I want to sort items of a class and collect them in Collection-Classes that beside a List-Member also contain further information that are necessary for the sorting process.
The following example is a a very simplified example for my problem. Although it doesn't make sense, I hope it still can help to understand my Question.
type ItemType = Odd|Even //realworld: more than two types possible
type Item(number) =
member this.number = number
member this.Type = if (this.number % 2) = 0 then Even else Odd
type NumberTypeCollection(numberType:ItemType , ?items:List<Item>) =
member this.ItemType = numberType
member val items:List<Item> = defaultArg items List.empty<Item> with get,set
member this.append(item:Item) = this.items <- item::this.items
let addToCollection (collections:List<NumberTypeCollection>) (item:Item) =
let possibleItem =
collections
|> Seq.where (fun c -> c.ItemType = item.Type) //in my realworld code, several groups may be returned
|> Seq.tryFind(fun _ -> true)
match possibleItem with
|Some(f) -> f.append item
collections
|None -> NumberTypeCollection(item.Type, [item]) :: collections
let rec findTypes (collections:List<NumberTypeCollection>) (items:List<Item>) =
match items with
| [] -> collections
| h::t -> let newCollections = ( h|> addToCollection collections)
findTypes newCollections t
let items = [Item(1);Item(2);Item(3);Item(4)]
let finalCollections = findTypes List.empty<NumberTypeCollection> items
I'm unsatisfied with the addToCollection method, since it requires the items in NumberTypeCollection to be mutual. Maybe there are further issues.
What can be a proper functional solution to solve this issue?
Edit: I'm sorry. May code was too simplified. Here is a little more complex example that should hopefully illustrate why I chose the mutual class-member (although this could still be the wrong decision):
open System
type Origin = Afrika|Asia|Australia|Europa|NorthAmerika|SouthAmerica
type Person(income, taxrate, origin:Origin) =
member this.income = income
member this.taxrate = taxrate
member this.origin = origin
type PersonGroup(origin:Origin , ?persons:List<Person>) =
member this.origin = origin
member val persons:List<Person> = defaultArg persons List.empty<Person> with get,set
member this.append(person:Person) = this.persons <- person::this.persons
//just some calculations to group people into some subgroups
let isInGroup (person:Person) (personGroup:PersonGroup) =
let avgIncome =
personGroup.persons
|> Seq.map (fun p -> float(p.income * p.taxrate) / 100.0)
|> Seq.average
Math.Abs ( (avgIncome / float person.income) - 1.0 ) < 0.5
let addToGroup (personGroups:List<PersonGroup>) (person:Person) =
let possibleItem =
personGroups
|> Seq.where (fun p -> p.origin = person.origin)
|> Seq.where (isInGroup person)
|> Seq.tryFind(fun _ -> true)
match possibleItem with
|Some(f) -> f.append person
personGroups
|None -> PersonGroup(person.origin, [person]) :: personGroups
let rec findPersonGroups (persons:List<Person>) (personGroups:List<PersonGroup>) =
match persons with
| [] -> personGroups
| h::t -> let newGroup = ( h|> addToGroup personGroups)
findPersonGroups t newGroup
let persons = [Person(1000,20, Afrika);Person(1300,22,Afrika);Person(500,21,Afrika);Person(400,20,Afrika)]
let c = findPersonGroups persons List.empty<PersonGroup>
What I may need to emphasize: There can be several different groups with the same origin.
Tomas' solution using groupby is the optimal approach if you want to generate your collections only once, it's a simple and concise.
If you want to be able to add/remove items in a functional, referentially transparent style for this type of problem, I suggest you move away from seq and start using Map.
You have a setup which is fundamentally dictionary-like. You have a unique key and a value. The functional F# equivalent to a dictionary is a Map, it is an immutable data structure based on an AVL tree. You can insert, remove and search in O(log n) time. When you append/remove from the Map, the old Map is maintained and you receive a new Map.
Here is your code expressed in this style
type ItemType =
|Odd
|Even
type Item (number) =
member this.Number = number
member this.Type = if (this.Number % 2) = 0 then Even else Odd
type NumTypeCollection = {Items : Map<ItemType, Item list>}
/// Functions on NumTypeCollection
module NumberTypeCollection =
/// Create empty collection
let empty = {Items = Map.empty}
/// Append one item to the collection
let append (item : Item) numTypeCollection =
let key = item.Type
match Map.containsKey key numTypeCollection.Items with
|true ->
let value = numTypeCollection.Items |> Map.find key
let newItems =
numTypeCollection.Items
|> Map.remove key
|> Map.add key (item :: value) // append item
{Items = newItems }
|false -> {Items = numTypeCollection.Items |> Map.add key [item]}
/// Append a list of items to the collections
let appendList (item : Item list) numTypeCollection =
item |> List.fold (fun acc it -> append it acc) numTypeCollection
Then call it using:
let items = [Item(1);Item(2);Item(3);Item(4)]
let finalCollections = NumberTypeCollection.appendList items (NumberTypeCollection.empty)
If I understand your problem correctly, you're trying to group the items by their type. The easiest way to do that is to use the standard library function Seq.groupBy. The following should implement the same logic as your code:
items
|> Seq.groupBy (fun item -> item.Type)
|> Seq.map (fun (key, values) ->
NumberTypeCollection(key, List.ofSeq values))
Maybe there are further issues.
Probably. It's difficult to tell, since it's hard to detect the purpose of the OP code... still:
Why do you even need an Item class? Instead, you could simply have a itemType function:
let itemType i = if i % 2 = 0 then Even else Odd
This function is referentially transparent, which means that you can replace it with its value if you wish. That makes it as good as a property getter method, but now you've already saved yourself from introducing a new type.
Why define a NumberTypeCollection class? Why not a simple record?
type NumberTypeList = { ItemType : ItemType; Numbers : int list }
You can implement addToCollection like something like this:
let addToCollection collections i =
let candidate =
collections
|> Seq.filter (fun c -> c.ItemType = (itemType i))
|> Seq.tryHead
match candidate with
| Some x ->
let x' = { x with Numbers = i :: x.Numbers }
collections |> Seq.filter ((<>) x) |> Seq.append [x']
| None ->
collections |> Seq.append [{ ItemType = (itemType i); Numbers = [i] }]
Being immutable, it doesn't mutate the input collections, but instead returns a new sequence of NumberTypeList.
Also notice the use of Seq.tryHead instead of Seq.tryFind(fun _ -> true).
Still, if you're attempting to group items, then Tomas' suggestion of using Seq.groupBy is more appropriate.

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);;

Resources