I'm trying to read two integers which are going to be taken as input from the same line. My attempt so far:
let separator: char =
' '
Console.ReadLine().Split separator
|> Array.map Convert.ToInt32
But this returns a two-element array and I have to access the individual indices to access the integers. Ideally, what I would like is the following:
let (a, b) =
Console.ReadLine().Split separator
|> Array.map Convert.ToInt32
|> (some magic to convert the two element array to a tuple)
How can I do that?
I'm afraid there's no magic. You have to explicitly convert into a tuple
let a, b =
Console.ReadLine().Split separator
|> Array.map int
|> (fun arr -> arr.[0], arr.[1])
Edit: you can use reflection as #dbc suggested but that's slow and probably overkill for what you're doing.
Related
I have an array with multiple lists inside and want to write the values to an CSV
unfortunately I cannot figure out how to write the Array.mapi.
Any ideas?
let temp = [|title;body;ordinariePris;extraPris;inkopPris;images;allValues|]
let lines2 =
temp
|> Array.mapi (fun idx (t,b,op,ep,ip,i,av) ->
sprintf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" title body ordinariePris extraPris inkopPris images allValues
)
let header = "title\tbody\tordinariePris\textraPris\tinkopPris\tallValues"
System.IO.File.WriteAllLines("test.csv",
Array.append [| header|]lines2, Encoding.UTF8)
thanks in advance
The problem with your code is that mapi indexes over the elements of temp, which means that you're only getting one column at a time. You want to retrieve one row at a time.
The following instead transposes your list of lists, so that indexing does what you want. It's also a little more generic-- you don't actually need to know the number of columns ahead of time. It does, however, assume that every column has the same number of elements in it.
let col1 = ["a"; "b"; "c"]
let col2 = ["1"; "2"; "3"]
let col3 = ["x"; "y"; "z"]
let cols = [col1; col2; col3]
let transpose(xs: string list list) : string list list =
[0 .. xs.[0].Length - 1] |> List.map (fun i ->
xs |> List.rev |> List.fold (fun acc col -> col.[i] :: acc) []
)
let stringify_rows(xs: string list list) : string list =
xs |> List.map (fun row -> System.String.Join(",", row))
System.IO.File.WriteAllLines("test.csv", cols |> transpose |> stringify_rows)
Note that this approach does not use an array of lists, since there was no obvious reason that you had to use an array. WriteAllLines will happily accept any IEnumerable.
If you do a lot of CSV writing, I recommend using a library like CsvHelper, since correctly handling when to quote or escape cell contents can be tricky.
I'm working on a homework assignment. And I'm trying to learn F# so I don't want any shortcuts besides using basic things like List.Map or lambdas or something.
I'm trying to process a list of tuples, but I'm having trouble accessing the tuples in the list.
I want to take the list of tuples, add up the numbers in each tuple, and return that number, printing it out each time.
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
let getSum (a,b,c) =
a+b+c
let rec printSum tpList =
let total = 0
match tpList with
| [] -> total //return 0 if empty list
| hd::tl ->
print (getSum hd)
The first thing you want to do is map your tuples through the getSum function. This can be done very simply by piping the list of tuples into List.map getSum. Then you want to print each element in the list, so you pipe the result of List.map getSum into List.iter with the function printfn "%d". This works because of the functions having curried parameters. printfn "%d" applies the "%d" parameter to printfn and returns a function taking an integer, which it then prints. The whole thing would look like this:
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
let getSum (a,b,c) =
a + b + c
let printSum tpList =
tpList |> List.map getSum |> List.iter (printfn "%d")
This prints:
9
15
21
We can even simplify the function further if we take advantage of function composition (the >> operator). Notice that printSum takes tpList as its parameter, and then just uses it as input to two functions that are pipelined together. Since pipelining just takes the output of one function and passes it as the last parameter of another function, all we really need to do is compose the function List.map getSum, which takes a list of int 3-tuples and returns a list of ints with List.iter (printfn "%d"), which takes a list of ints and returns unit. That would look like this:
let printSum = List.map getSum >> List.iter (printfn "%d")
This will print the same results, but is a simpler way of expressing the function.
F# has imperative loops as well. In this case I think an imperative loop matches the problem most idiomatically.
let listTup = [(2,3,4); (4,5,6); (6,7,8)]
for a,b,c in listTup do
let sum = a + b + c
printfn "%d" sum
I've got an array of strings, call it a, where each individual string represents a number. I also have a function f : int -> int -> int, which I want to use to "reduce a to a single number". I would like to write:
a |> Array.reduce (fun x y -> f (int32 x) (int32 y))
but this does not work, because the type of "reduce" forbids me from returning integers from f (since a is an array of strings)
Is there a functional way to make this work without having to return a
string from f or casting the string array to an int array beforehand?
using Array.reduce mapping first
If you don't want to adapt your f to handle strings, and you want to use Array.reduce then yeah I guess you should convert first (and to be honest: it seems easier than doing it manually with your wrapper-lambda) - so why not just use
a
|> Array.map int32
|> Array.reduce f
instead?
if you are concerned with the overhead of producing an intermediate Array you can always switch Array. with Seq. to to it lazily:
a |> Seq.map int32 |> Seq.reduce f
using Array.fold
aside from that you can always fold to your hearts desire:
a |> Array.fold (fun n s -> n + int32 s) 0
so you might call this more functional or not ;)
I am using F# Canopy to complete some web testing. I am trying to create and load a random number with or without letters, not that important and use it to paste to my website.
The code I am currently using is
let genRandomNumbers count =
let rnd = System.Random()
List.init count
let l = genRandomNumbers 1
"#CompanyName" << l()
The #CompanyName is the ID of the element I am trying to pass l into. As it stands I am receiving the error 'The expression was expected to have type string but here it has type a list.
Any help would be greatly appreciated.
The << operator in canopy writes a string to the selector (I haven't used it but the documentation looks pretty clear), but your function returns a list. If you want the random string to work, you could do something like this (not tested code)
let randomNumString n = genRandomNumbers n |> List.map string |> List.reduce (+)
This maps your random list to strings then concats all the strings together using the first element as the accumulator seed. You could also do a fold
let randomNumString n = genRandomNumbers n
|> List.fold (fun acc i -> acc + (string i)) ""
Putting it all together
let rand = new System.Random()
let genRandomNumbers count = List.init count (fun _ -> rand.Next())
let randomNumString n = genRandomNumbers n |> List.map string |> List.reduce (+)
"#CompanyName" << (randomNumString 1)
In general, F# won't do any type promotion for you. Since the << operator wants a string on the right hand side, you need to map your list to a string somehow. That means iterating over each element, converting the number to a string, and adding all the elements together into one final string.
The following code lists the set of keys found in a dictionary sequence (each dict is basically a row from a database). (I want to convert the keys to a set so I can compare 2 db tables)
for seqitem in tblseq do
let keyset = seqitem.Keys |> Set.ofSeq // works correctly
printfn ">>> List: %A; Item Type: %A" keyset
Rather than print the keyset however I want to return it from a function but am having a problem with type inference. Tried the following but it does not work;
What I want to do is return these values as either an array of list (rather than print them)
let get_keyset tblseq =
tblseq |> Seq.iter (fun x ->
x.Keys |> Set.ofSeq
)
What am I missing here?
Using Seq.map as ildjarn suggests is one option (you may want to add Array.ofSeq to the end to get array of sets as you say in your qurestion).
An alternative approach is to use array comprehension:
let get_keyset (tblseq:seq<System.Collections.Generic.Dictionary<_, _>>) =
[| for x in tblseq -> x.Keys |> Set.ofSeq |]
The notation [| .. |] says that you want to create an array of elements and the expression following -> specifies what should be produced as an element. The syntax is essentially just a nicer way for writing Seq.map (although it supports more features).
You can also use this syntax for creating sets (instead of calling Set.ofSeq). In this case, it doesn't make much sense, because Set.ofSeq is faster and sorhter, but sometimes it is quite neat option. It allows you to avoid type annotations, because you can get key of a dictionary using KeyValue pattern:
let get_keyset tblseq =
[| for x in tblseq ->
set [ for (KeyValue(k, v)) in x -> k ] |]
Use Seq.map rather than Seq.iter:
let get_keyset tblseq =
tblseq
|> Seq.map (fun (x:Dictionary<_,_>) -> x.Keys |> set)
|> Array.ofSeq