Given a list kinda like (simplified):
type foo(n:string,m:string,l:string) =
member f.name=n
member f.val1=m
member f.val2=l
let dates = [
foo("a","aa","aaa")
foo("b","bb","bbb")
]
How can a a immutable dictionary-type structure (eg, Map, IDictionary...any others?) of the form key:foo.name,value:foo be made?
My best guess was
let fooDict = for f in foo do yield f.name,f
But that for-comprehension syntax can only be used to make a list, array, or seq?
To create an immutable dictionary (the interface is mutable, but will throw an exception if you try to modify it)
[ for f in dates -> f.name,f ] |> dict
or
dates |> Seq.map (fun f -> f.name, f) |> dict
To create an immutable Map:
[ for f in dates -> f.name,f ] |> Map.ofSeq
or
dates |> Seq.map (fun f -> f.name, f) |> Map.ofSeq
I ended up with the more verbose (and ignorant of pipes):
Map.ofList([for f in foo do yield (f.name,f)])
Check out the dict function -- it takes a sequence of key-value tuples and creates an immutable dictionary from them.
With a list like the one you provided, you could use Seq.map to create the sequence, then pipe it into dict.
Related
I have a text file that contains the following and I need to retrieve the value assigned to taskId, which in this case is AWc34YBAp0N7ZCmVka2u.
projectKey=ProjectName
serverUrl=http://localhost:9090
serverVersion=10.5.32.3
strong text**interfaceUrl=http://localhost:9090/interface?id=ProjectName
taskId=AWc34YBAp0N7ZCmVka2u
taskUrl=http://localhost:9090/api/ce/task?id=AWc34YBAp0N7ZCmVka2u
I have two different ways of reading the file that I've wrote.
let readLines (filePath:string) = seq {
use sr = new StreamReader (filePath)
while not sr.EndOfStream do
yield sr.ReadLine ()
}
readLines (FindFile currentDirectory "../**/sample.txt")
|> Seq.iter (fun line ->
printfn "%s" line
)
and
let readLines (filePath:string) =
(File.ReadAllLines filePath)
readLines (FindFile currentDirectory "../**/sample.txt")
|> Seq.iter (fun line ->
printfn "%s" line
)
At this point, I don't know how to approach getting the value I need. Options that, I think, are on the table are:
use Contains()
Regex
Record type
Active Pattern
How can I get this value returned and fail if it doesn't exist?
I think all the options would be reasonable - it depends on how complex the file will actually be. If there is no escaping then you can probably just look for = in the line and use that to split the line into a key value pair. If the syntax is more complex, this might not always work though.
My preferred method would be to use Split on string - you can then filter to find values with your required key, map to get the value and use Seq.head to get the value:
["foo=bar"]
|> Seq.map (fun line -> line.Split('='))
|> Seq.filter (fun kvp -> kvp.[0] = "foo")
|> Seq.map (fun kvp -> kvp.[1])
|> Seq.head
Using active patterns, you could define a pattern that takes a string and splits it using = into a list:
let (|Split|) (s:string) = s.Split('=') |> List.ofSeq
This then lets you get the value using Seq.pick with a pattern matching that looks for strings where the substring before = is e.g. foo:
["foo=bar"] |> Seq.pick (function
| Split ["foo"; value] -> Some value
| _ -> None)
The active pattern trick is quite neat, but it might be unnecessarily complicating the code if you only need this in one place.
I am completely at loss why this code doesn't mutate a member variable in a sequence of types:
for p in prescrs do
p.ATC <- "A"
for c in p.Drug.Components do
for s in c.Substances do
s.DoseTotal.Adjust <- adjustKg
s.DoseTotal.Time <- "DAY"
s.DoseTotal.Unit <- s.DrugConcentration.Unit
s.DoseRate.Adjust <- adjustKg
s.DoseRate.Time <- "DAY"
s.DoseRate.Unit <- s.DrugConcentration.Unit
prescrs is a sequence of Prescriptions which is a very simple 'POCO' defined as a type with member values. I don't have clue why this doesn't work.
I tried a simple test case like:
type IterTest () =
member val Name = "" with get, set
member val IterTests = [] |> List.toSeq : IterTest seq with get, set
let iterseq =
[
new IterTest(Name = "Test1")
new IterTest(Name = "Test2")
]
|> List.toSeq
iterseq |> Seq.iter(fun x -> x.IterTests <- iterseq)
iterseq |> Seq.iter(fun x ->
x.IterTests
|> Seq.iter(fun x' -> x'.Name <- "itered"))
But here the result is as expected. So, can't even quite reproduce my problem???
Found a solution (without really understanding the problem above). When I first convert the prescrs sequence to a list like:
let prescrs = prescrs |> Seq.toList
and then do the imperative looping, properties do get mutated.
Try this sample:
type Mutable() =
member val Iterated = false with get, set
let muts = Seq.init 5 (fun _ -> printfn "init"; Mutable())
let muts2 = muts // try again with let muts2 = muts |> List.ofSeq
printfn "Before iter"
for a in muts2 do
printfn "iter"
a.Iterated <- true
printfn "After iter"
muts2 |> List.ofSeq
and check how iter and init are interleaved.
Seqs are lazy, but are not cached once computed. So even if you imperatively try to mutate some of the elements in your prescrs sequence, it all goes away once you pull prescrs again. If you change prescrs into a concrete collection type like list before doing the mutation, you no longer hit the same problem. Note that things might get even trickier if what you have is a seq inside a seq inside a seq.
The best idea would be to avoid mutation in the first place though.
I have a function that returns a string[].
let asyncScrape url allParameters =
allParameters
|> Seq.map(fun v ->
yearAndClassResultsAsync url v)
|> Async.Parallel
|> Async.RunSynchronously
I want to iterate through that string array, sending each string to a method called resultsBody (that returns a seq), and then finally returning an single sequence that is the concatenation of the results from resultsBody.
I tried doing something like below, but I'm rather lost as it returns:
seq<string[]>[]
and I just want a single combined
seq<string[]>
My attempts so far:
let parseSite html =
Array.mapi (fun s -> resultsBody) html
Simplified, I think your problem is that you have a nested sequence of strings and you want to get as much parallelism as possible, rather than just at the innermost layer of the nesting.
One way you can do this is to also nest the Async.Parallel calls before calling Async.RunSynchronously. Here's a simple example of the technique:
let squareInt n = async { return n*n }
let inParallel (seqOfseqOfInts : seq<seq<int>>) =
seqOfseqOfInts
|> Seq.map // deal with each inner seq of ints
(fun (seqOfInts : seq<int>) ->
seqOfInts
|> Seq.map squareInt
|> Async.Parallel // this gives us Async<int[]>
) // this gives us seq<Async<int[]>>
|> Async.Parallel // this gives us Async<int[][]>
|> Async.RunSynchronously // this gives us int[][]
I have a string "ABCDEFG". I want to convert that into a string array with the contents: [|"ABC"; "BCD"; "CDE"; "DEF"; "EFG"|]
I first thought about using a loop. Then I thought about using a recursive function. Finally, I was wondering if there is a function in the F# spec like Seq.Fold I can use.
Take a look at Seq.windowed, should do what you want.
> "ABCDEFG" |> Seq.windowed 3 |> Seq.map (fun a -> System.String a);;
val it : seq<System.String> = seq ["ABC"; "BCD"; "CDE"; "DEF"; ...]
I'm writing a quick DB perf test, and chose F# so I can get more practice.
I've created a method, measureSelectTimes, which has the signature Guid list * Guid list -> IDbCommand -> TimeSpan * TimeSpan.
Then, I call it:
let runTests () =
let sqlCeConn : IDbConnection = initSqlCe() :> IDbConnection
let sqlServerConn : IDbConnection = initSqlServer() :> IDbConnection
let dbsToTest = [ sqlCeConn; sqlServerConn ]
let cmds : seq<IDbCommand> = dbsToTest |> Seq.map initSchema
let ids : seq<Guid list * Guid list> = cmds |> Seq.map loadData
let input = Seq.zip ids cmds
let results = input |> Seq.map (fun i -> measureSelectTimes (fst i) (snd i))
// ...
I've annotated explicitly with types to clarify.
What I can't figure out is how to call measureSelectTimes without the lambda. I'd like to partially apply the ids to it like this: ids |> Seq.map measureSelectTimes but then I don't know what to do with the resulting partially applied functions to then map onto the cmds. What's the syntax for this?
You can use Seq.map2:
Seq.map2 measureSelectTimes ids cmds
Or
(ids, cmds) ||> Seq.map2 measureSelectTimes
Your measureSelectTimes function takes two arguments as separate arguments, but you instead need a function that takes them as a tuple. One option is to just change the function to take a tuple (if it is logical for the arguments to be tupled).
Alternative, you can write a cobinator that turns a function taking two arguments into a function taking tuple. This is usually called uncurry and it exists in some functional language:
let uncurry f (a, b) = f a b
Then you can write:
input |> Seq.map (uncurry measureSelectTimes)
This looks okay for a simple use like this, but I think that using combinators too much in F# is not a good idea as it makes code difficult to read for less experienced functional programmers. I would probably write something like this (because I find that more readable):
[ for (time1, time2) in input -> measureSelectTimes time1 time2 ]
One approach is to change the signature of measureSelectTimes to
(Guid list * Guid list) * IDbCommand -> TimeSpan * TimeSpan
Then you can change the map call to
let results = input |> Seq.map measureSelectTimes
// or
let results = Seq.map measureSelectTimes input