I have a Map<string,int> where I want to sum all values.
It will be used as word dictionary in a toy spelling corrector which is described here.
Naive attempt
let wc myMap =
Map.toSeq myMap
|> Seq.sumBy (fun kvp -> (snd kvp))
error FS0071 Consider adding further type constraints
Attempt 1
type MapFn = Map<string,int> -> int
let wc:MapFn = (function wm ->
Map.toSeq wm
|> Seq.sumBy (fun kvp -> (snd kvp)))
Attempt 2
type WordMap = WordMap of Map<string,int>
let wc (WordMap wm) =
Map.toSeq wm
|> Seq.sumBy (fun kvp -> (snd kvp))
Both attempts work, however I would like code tidier. Like sample code in Python sum(WORDS.values()).
WORDS = Counter(words(open('big.txt').read()))
def P(word, N=sum(WORDS.values())):
return WORDS[word] / N
Also wonder if type Map<string,int> is the best choice for my dictionary.
Map element is a key/value pair (KeyValuePair<string, int> in your example), not a tuple. So you should use kvp.Value instead of snd kvp, for example:
myMap |> Seq.sumBy(fun item -> item.Value )
or using Map.fold
myMap |> Map.fold (fun state key value -> state + value) 0
Related
I have an integer array in which I want to send every two elements from it to the constructor of another function.
Something like intArray |> Array.map (fun x, y -> new Point(x, y))
Is this possible? I'm new to F# and functional programming so I'm trying to avoid just looping through every 2 items in the array and adding the point to a list. I hope that's reasonable.
If using F# 4.0, use Gustavo's approach. For F# 3, you can do:
intArray
|> Seq.pairwise // get sequence of tuples of element (1,2); (2,3); (3,4); (4,5) etc
|> Seq.mapi (fun i xy -> i, xy) // combine the index with the tuple
|> Seq.filter (fun (i,_) -> i % 2 = 0) // Filter for only the even indices to get (1,2); (3,4)
|> Seq.map (fun xy -> Point xy) // make a point from the tuples
|> Array.ofSeq // convert back to array
You can use Array.chunkBySize:
intArray
|> Array.chunkBySize 2
|> Array.map (function
| [|x; y|] -> new Point (x, y)
| _ -> failwith "Array length is not even.")
An alternative solution to the existing answers, would be to write a custom function, that creates a list/an array of tuples using pattern matching:
let chunkify arr =
let rec chunkify acc lst =
if (List.length lst) > 1 then (* proceed if there are at least two elements *)
match lst with
(* save every constructed pair, until the input is not empty *)
| h1 :: h2 :: tail -> chunkify ([(h1, h2)] # acc) tail
| _ -> acc (* else return the list of pairs *)
else (* return the list of pairs *)
acc
chunkify List.empty (List.ofSeq arr) |> List.rev |> Array.ofSeq
The function can be then used like this:
// helper
let print = (fun (x:'a, y:'a) -> printfn "new Object(%A,%A)" x y)
// ints
[|1;2;3;4;5;6|]
|> chunkify
|> Array.iter print
// strings
[|"a";"b";"c";"d";"e"|]
|> chunkify
|> Array.map print
|> ignore
The output is:
new Object(1,2)
new Object(3,4)
new Object(5,6)
new Object("a","b")
new Object("c","d")
This solution/approach uses pattern matching with lists.
I have a map reduce code for which I group in each of the threads by some key and then in the reduce part merge the results. My current approach is to search for an specific key index in the accumulator and then mapi to retrieve the combined result only for this key, leaving the rest unmodified:
let rec groupFolder sequence acc =
match sequence with
| (by:string, what) :: rest ->
let index = acc |> Seq.tryFindIndex( fun (byInAcc, _) -> byInAcc.Equals(by) )
match index with
| Some (idx) ->
acc |> Seq.mapi( fun i (byInAcc, whatInAcc) -> if i = idx then (by, (what |> Array.append whatInAcc) ) else byInAcc, whatInAcc )
|> groupFolder rest
| None -> acc |> Seq.append( seq{ yield (by, what) } )
|> groupFolder rest
My question is, is it a more functional way to achieve just this?
As an example input to this reducer
let GroupsCommingFromMap = [| seq { yield! [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |] }, seq { yield! [|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |] } |];;
GroupsCommingFromMap |> Seq.reduce( fun acc i ->
acc |> groupFolder (i |> Seq.toList))
the expected result should contain all key1..key3 each with the array 1..6
From the code you posted, it is not very clear what you're trying to do. Could you include some sample inputs (together with the output that you would like to get)? And does your code actually work on any of the inputs (it has incomplete pattern match, so I doubt that...)
Anyway, you can implement key-based map reduce using Seq.groupBy. For example:
let mapReduce mapper reducer input =
input
|> Seq.map mapper
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) ->
k, vs |> Seq.map snd |> Seq.reduce reducer)
Here:
The mapper takes a value from the input sequence and turns it into key value pair. The mapReduce function then groups the values using the key
The reducer is then used to reduce all values associated with each key
This lets you create a word count function like this (using simple mapper that returns the word as the key with 1 as a value and reducer that just adds all the numbers):
"hello world hello people hello world".Split(' ')
|> mapReduce (fun w -> w, 1) (+)
EDIT: The example you mentioned does not really have "mapper" part, but instead it has array of arrays as an input - so perhaps it is easier to write this directly using Seq.groupBy like this:
let GroupsCommingFromMap =
[| [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |]
[|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |] |]
GroupsCommingFromMap
|> Seq.concat
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) -> k, vs |> Seq.map snd |> Array.concat)
I have an array like so:
let array = [|"A";"B";"C";"D"|]
I want to create an array based on the original array's value and the index like this:
[|"A",0;"B",1;"C",2;"D",4|]
If there a way to do this without resorting to a loop? I was thinking Seq.mapi or Seq.fold but I am not having much success with them....
Thanks in advance.
Function Array.mapi and Array.collect should do the trick:
array |> Array.mapi (fun i e -> (i, e)) |> Array.collect (fun (a, b) -> [|string a;b|])
Evaluation of this expression yields:
val it : string [] = [|"0"; "A"; "1"; "B"; "2"; "C"; "3"; "D"|]
However, I have converted integer to the string. Otherwise compiler can't infer type of the array.
If you need to have an array with elements of different type you can use Discriminated Union type.
Here is an example:
type ArrayElement =
| Int of int
| String of string
[|"A";"B";"C";"D"|] |> Array.mapi (fun i e -> (i, e)) |> Array.collect (fun (a, b) -> [|Int(a);String(b)|])
As Valera says, the answer lies in Array.mapi.
However I notice that your desired output isn't an array of differing types (strings and ints) as Valera suggests, but is an array of tuples of string*int.
In the light of this the answer is simpler:
let array = [|"A";"B";"C";"D"|]
array
|> Array.mapi (fun i s -> s, i)
(BTW I think your last index should be 3 not 4.)
I have a map X and I'm trying to get a set of the keys satisfying a certain condition, something like this:
Map.Keys X
|> Set.filter (fun x -> ...)
...but I cannot find the way to get the keys from F#'s Map collection.
Convert your map to sequence of tuples (key,value) first and then map it to a sequence of just keys:
map |> Map.toSeq |> Seq.map fst
FSI sample:
>Map.ofList[(1,"a");(2,"b")] |> Map.toSeq |> Seq.map fst;;
val it : seq<int> = seq [1; 2]
Or alternatively, as ordering of keys likely does not matter you may use more eager method returning the list of all keys. It is also not hard to make it into extension method keys of Microsoft.FSharp.Collections.Map module:
module Map =
let keys (m: Map<'Key, 'T>) =
Map.fold (fun keys key _ -> key::keys) [] m
In F# 6.0, Map collection has now a Keys property.
OLD ANSWER:
Most readable (and probably most efficient, due to not needing previous conversions to Seq or mapping) answer:
let Keys(map: Map<'K,'V>) =
seq {
for KeyValue(key,value) in map do
yield key
} |> Set.ofSeq
For a set of keys you could just do:
let keys<'k, 'v when 'k : comparison> (map : Map<'k, 'v>) =
Map.fold (fun s k _ -> Set.add k s) Set.empty map
Suppose I have a list of tupples like these :
[("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
And I would like to do some kind of groupby how do I handle that?
In pseudocode-SQL it should look something like this :
SELECT fst(tpl), sum(lst(tpl)) FROM [TupplesInList] GROUP BY fst(tpl)
yielding
[("A",22); ("B",1); ("C",3)]
I could make a Dictionary and add the ints if the key exist, but I can hardly believe that would be the best solution in a language as expressive as F#.
One solution:
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
tuples
|> Seq.groupBy fst
|> Seq.map (fun (key, values) -> (key, values |> Seq.sumBy snd))
Edit: ...or without piping:
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
Seq.map (fun (key, group) -> key, Seq.sumBy snd group)
(Seq.groupBy fst tuples)
To expand on Johan's answer, I tend to do this sort thing alot and so have made the following generalized function.
let group_fold key value fold acc seq =
seq |> Seq.groupBy key
|> Seq.map (fun (key, seq) -> (key, seq |> Seq.map value |> Seq.fold fold acc))
Which works for your tuple case as seen below
let tuples = [("A",12); ("A",10); ("B",1); ("C",2); ("C",1)]
let regular = group_fold fst snd (+) 0 tuples
let piped = tuples |> group_fold fst snd (+) 0
but will also work with other seqences like a list of strings
let strings = ["A12"; "A10"; "B1"; "C2"; "C1"]
let regular = group_fold (fun (x : string) -> x.[0]) (fun (x : string) -> int x.[1..]) (+) 0 strings
let piped = strings |> group_fold (fun x -> x.[0]) (fun x -> int x.[1..]) (+) 0