Converting a dictionary (passed from C#) to a string in F# - f#

Edit:
it is solved. The answer is:
|> Seq.sortBy (fun (KeyValue(k,v)) -> k)
|> Seq.map (fun (KeyValue(k,v)) -> (sprintf "%s:%s" k v))
|> String.concat ","
Original question:
I'm trying to do the F# version of this:
var postData = "{"
+ string.Join(
",",
sortedParameters.Select(item => $"\"{item.Key}\":\"{item.Value}\"").ToList())
+ "}";
I have the following dictionary, coming from C#:
{
{ "a", "1" },
{ "c", "3" },
{ "b", "2" },
}
I would like to sort it, then output all the values in a string like this:
"a":"1", "b":"2", "c":"3"
so, I am starting like this:
let Test (values : Dictionary<string, string>) : string =
|> Seq.sortBy (fun (KeyValue(k,v)) -> k)
|> Seq.iter (fun (KeyValue(k,v)) -> (sprintf "%s:%s" k v))
but the last part doesn't compile:
Compilation error (line 13, col 50): Type mismatch. Expecting a string -> string -> unit but given a string -> string -> string The type 'unit' does not match the type 'string'
Compilation error (line 13, col 60): This expression was expected to have type string but here has type int
I do not understand the error message. My assumption is that this would return me a list of strings like:
{
"a:1", "b:2", "c:3"
}
that I would have to join in a string, in the next step
obviously, it's not done like that :)
what did I miss?

let showDict (x : Dictionary<string, string>) : string =
x
|> Seq.sortBy (fun (KeyValue (k, v)) -> k)
|> Seq.map (fun (KeyValue (k, v)) -> $"%s{k}:%s{v}"))
|> String.concat ", "

Related

F# How to print out a sequence in a pipe forward sequence?

Is it possible to print out a sequence in a pipe forward sequence? I have the following code:
let rec crawlPage (page : String, nestingLevel : int) : seq<string> =
System.Threading.Thread.Sleep 1200
//printfn "URL: %s" page
//printfn "Nesting Level: %i \n" nestingLevel
HtmlDocument.Load(page)
|> fun m -> m.CssSelect("a")
|> List.map(fun a -> a.AttributeValue("href"))
|> Seq.distinctBy id
|> Seq.filter (fun x -> x.Contains baseUrl1)
//|> Seq.map (printfn "%A") // I would like to be able to do something like this.
|> Seq.map (fun x -> https + x)
|> Seq.map (fun x ->
match nestingLevel with
| _ when (nestingLevel > 0) -> crawlPage(x, (nestingLevel - 1))
| _ -> Seq.singleton x)
|> Seq.concat
|> Seq.distinctBy id
I was able to work around it by writing a helper function like follows:
let strPrint (str : string) : string =
printfn "%s" str
str
I would rather just use the pipe forwarding if possible though.
Sure, you can just do the exact same thing inline, with an anonymous function:
...
|> Seq.map (fun str ->
printfn "%s" str
str)
|>
...
As a rule, if you have let f x = e, you can always replace any occurrence of f with its anonymous equivalent (fun x -> e)

F# match with query results. Is there an elegant way to do this?

I have an result of a JObject type, from parsing json:
let j = JObject.Parse x
the code I have to do is like:
if j = null then
... do stuff
else if j["aa"] <> null then
... do stuff
else if j["bb"] <> null then
... do stuff
else if j["cc"] <> null and j["dd"] <> null then
... do stuff
is there a clean way to do this match?
doing statements like
| _ when j.["error"] <> null ->
doesn't seem super clean. Can this be done better?
If you create an active pattern that returns the matched JToken...
let (|NonNull|_|) prop (o : JObject) =
o.[prop] |> Option.ofObj
you could write something like:
let handleAA (a : JToken) = ()
match JObject.Parse "{}" with
| null -> () // ...
| NonNull "aa" a -> handleAA a
| NonNull "bb" b & NonNull "cc" c -> ()
| _ -> () // all other
Update
If you need more power, Active Patterns galore...
let (|J|_|) prop (o : obj) =
match o with
| :? JObject as o -> o.[prop] |> Option.ofObj
| _ -> None
let (|Deep|_|) (path : string) (o : obj) =
let get t p = t |> Option.bind (fun t -> (``|J|_|``) p t)
match o with
| :? JToken as t ->
path.Split('.') |> Array.fold get (Option.ofObj t)
| _ -> None
... some helpers ...
let jV (t : JToken) = t.Value<string>()
let handle t = jV t |> printfn "single: %s"
let handle2 a b = printfn "(%s, %s)" (jV a) (jV b)
... a parse function ...
let parse o =
match JsonConvert.DeserializeObject o with
| null -> printfn "null"
| J "aa" a -> handle a
| J "bb" b & J "cc" c -> handle2 b c
| J "bb" b & J "dd" _ -> handle b
| Deep "foo.bar" bar & Deep "hello.world" world -> handle2 bar world
| Deep "foo.bar" bar -> handle bar
| o -> printfn "val: %A" o
... and off we go:
parse "null" // null
parse "42" // val: 42L
parse "{ aa: 3.141 }" // single: 3.141
parse "{ bb: 2.718, cc: \"e\" }" // (2.718, e)
parse "{ bb: 2.718, dd: 0 }" // single: 2.718
parse "{ foo: { bar: \"baz\" } }" // single: baz
parse "{ foo: { bar: \"baz\" }, hello: { world: \"F#|>I❤\" } }" // (baz, F#|>I❤)
To do something for the first non-null value:
let j = JObject.Parse x
let doSomething s = printf "%A" s
if isNull j then
()
else
[ j.["aa"]; j.["bb"]; j.["cc"] ]
|> List.tryFind (fun s -> s |> Option.ofObj |> Option.isSome)
|> doSomething
Or do something for each non-null value:
let j = JObject.Parse x
let doSomething s = printf "%A" s
if isNull j then
()
else
[ j.["aa"]; j.["bb"]; j.["cc"] ]
|> List.choose (fun s -> s |> Option.ofObj)
|> List.iter doSomething
Or do something different (depending on which value is non-null) for the first non-null value:
let j = JObject.Parse x
let doSomethingA s = printf "%A" s
let doSomethingB s = printf "%A" s
let doSomethingC s = printf "%A" s
if isNull j then
()
else
[
j.["aa"], doSomethingA
j.["bb"], doSomethingB
j.["cc"], doSomethingC
]
|> List.tryFind (fun (s, _) -> s |> Option.ofObj |> Option.isSome)
|> Option.iter (fun (s, f) -> f s)
You could create an active pattern to match non-null values...
let (|NonNull|_|) = function null -> None | v -> Some v
...which would allow the following.
if isNull j then
//do stuff
else
match j.["aa"], j.["bb"], j.["cc"], j.["dd"] with
| NonNull aa, _, _, _ -> //do stuff
| _, NonNull bb, _, _ -> //do stuff
| _, _, NonNull cc, NonNull dd -> //do stuff
You could make a list of actions for each key so you could apply the null checking logic uniformly for each one.
let j = JObject.Parse x
let doStuff key value = printfn "%s=>%s" key value
If you wanted to apply doStuff for every key you could iterate though. This is your example but without the else so it does it for every key present.
["aa", doStuff
"bb", doStuff
"cc", doStuff]
|> List.iter (fun (key,action) ->
j.TryGetValue key
|> snd
|> Option.ofObj
|> Option.iter (action key))
Matching your example more closely where you only doStuff for the first key present might use choose to get only the valid values,actions.
["aa", doStuff
"bb", doStuff
"cc", doStuff]
|> Seq.choose (fun (key,action) ->
j.TryGetValue key
|> snd
|> Option.ofObj
|> Option.map (fun v -> action key v))
|> Seq.tryHead
This version also returns the result of the applied doStuff if there was a matching key and doStuff returned a value. This is abusing the lazy nature of Seq a little bit to only call the first value but you could also map to a function an call the result of Seq.tryHead.

F# |> (Pipeforward) 'block following let is unfinished' error

Would someone please help me understand why the code below gives me the error 'Block following let is unfinished. Expected an Expression'? The value of x is expected to be a string list and that is how F# sees it. So why does x not become a string list for use later in the function?
let fxProper (str : string) (values : obj[,]) =
let x =
values
|> Seq.cast<obj>
|> Seq.filter (fun x -> not (x :? ExcelEmpty))
|> Seq.map string
|> Seq.toList
You need to do something with the x value you just set
let fxProper (str : string) (values : obj[,]) =
let x =
values
|> Seq.cast<obj>
|> Seq.filter (fun x -> not (x :? ExcelEmpty))
|> Seq.map string
|> Seq.toList
x
should work.
This
let fxProper (str : string) (values : obj[,]) =
values
|> Seq.cast<obj>
|> Seq.filter (fun x -> not (x :? ExcelEmpty))
|> Seq.map string
|> Seq.toList
should work as well.
You're doing it right. The let binding for x is working properly. The error is telling you that your function fxProper isn't currently returning anything. If your intent is to return x then you need to add it at the end of fxProper like below, otherwise just add a dummy return value until you're finished writing your function.
let fxProper (str : string) (values : obj[,]) =
let x =
values
|> Seq.cast<obj>
|> Seq.filter (fun x -> not (x :? ExcelEmpty))
|> Seq.map string
|> Seq.toList
x //this returns the value of x from fxProper, this could also just the default value of whatever you actually want to return here

How to "convert" a Dictionary into a sequence in F#?

How do I "convert" a Dictionary into a sequence so that I can sort by key value?
let results = new Dictionary()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
let ranking =
results
???????
|> Seq.Sort ??????
|> Seq.iter (fun x -> (... some function ...))
A System.Collections.Dictionary<K,V> is an IEnumerable<KeyValuePair<K,V>>, and the F# Active Pattern 'KeyValue' is useful for breaking up KeyValuePair objects, so:
open System.Collections.Generic
let results = new Dictionary<string,int>()
results.Add("George", 10)
results.Add("Peter", 5)
results.Add("Jimmy", 9)
results.Add("John", 2)
results
|> Seq.sortBy (fun (KeyValue(k,v)) -> k)
|> Seq.iter (fun (KeyValue(k,v)) -> printfn "%s: %d" k v)
You may also find the dict function useful. Let F# do some type inference for you:
let results = dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
> val results : System.Collections.Generic.IDictionary<string,int>
Another option, which doesn't need a lambda until the end
dict ["George", 10; "Peter", 5; "Jimmy", 9; "John", 2]
|> Seq.map (|KeyValue|)
|> Seq.sortBy fst
|> Seq.iter (fun (k,v) -> ())
with help from https://gist.github.com/theburningmonk/3363893

How do I concatenate a list of strings in F#?

I'm trying this at the moment, but I haven't quite got the method signature worked out... anyone? messages is a field of seq[string]
let messageString = List.reduce(messages, fun (m1, m2) -> m1 + m2 + Environment.NewLine)
> String.concat " " ["Juliet"; "is"; "awesome!"];;
val it : string = "Juliet is awesome!"
Not exactly what you're looking for, but
let strings = [| "one"; "two"; "three" |]
let r = System.String.Concat(strings)
printfn "%s" r
You can do
let strings = [ "one"; "two"; "three" ]
let r = strings |> List.fold (+) ""
printfn "%s" r
or
let strings = [ "one"; "two"; "three" ]
let r = strings |> List.fold (fun r s -> r + s + "\n") ""
printfn "%s" r
I'd use String.concat unless you need to do fancier formatting and then I'd use StringBuilder.
(StringBuilder(), [ "one"; "two"; "three" ])
||> Seq.fold (fun sb str -> sb.AppendFormat("{0}\n", str))
just one more comment,
when you are doing with string, you'd better use standard string functions.
The following code is for EulerProject problem 40.
let problem40 =
let str = {1..1000000} |> Seq.map string |> String.concat ""
let l = [str.[0];str.[9];str.[99];str.[999];str.[9999];str.[99999];str.[999999];]
l |> List.map (fun x-> (int x) - (int '0')) |> List.fold (*) 1
if the second line of above program uses fold instead of concat, it would be extremely slow because each iteration of fold creates a new long string.
System.String.Join(Environment.NewLine, List.to_array messages)
or using your fold (note that it's much more inefficient)
List.reduce (fun a b -> a ^ Environment.NewLine ^ b) messages

Resources