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
Related
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
I have the following variable:
data:seq<(DateTime*float)>
and I want to do something like the following F# code but using Deedle:
data
|> Seq.groupBy (fun (k,v) -> k.Year)
|> Seq.map (fun (k,v) ->
let vals = v |> Seq.pairwise
let first = seq { yield v |> Seq.head }
let diffs = vals |> Seq.map (fun ((t0,v0),(t1,v1)) -> (t1, v1 - v0))
(k, diffs |> Seq.append first))
|> Seq.collect snd
This works fine using F# sequences but I want to do it using Deedle series. I know I can do something like:
(data:Series<DateTime*float>) |> Series.groupBy (fun k v -> k.Year)...
But then I need to take the within group year diffs except for the head value which should just be the value itself and then flatten the results into on series...I am bit confused with the deedle syntax
Thanks!
I think the following might be doing what you need:
ts
|> Series.groupInto
(fun k _ -> k.Month)
(fun m s ->
let first = series [ fst s.KeyRange => s.[fst s.KeyRange]]
Series.merge first (Series.diff 1 s))
|> Series.values
|> Series.mergeAll
The groupInto function lets you specify a function that should be called on each of the groups
For each group, we create series with the differences using Series.diff and append a series with the first value at the beginning using Series.merge.
At the end, we get all the nested series & flatten them using Series.mergeAll.
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.)
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