How do I iterate over a hashtable in F#? - f#

let dic = Environment.GetEnvironmentVariables()
dic
|> Seq.filter( fun k -> k.Contains("COMNTOOLS"))
fails to compile.
I've tried using Array.filter, Seq.filter, List.filter
I've tried getting just the dic.Keys to iterate over but F# doesn't seem to want me to coerce a KeyCollection into an IEnumerable.
I've tried upcasting the hashtable into an IEnumerable<KeyValuePair<string,string>>
How do I walk the hashtable that is returned from Environment.GetEnvironmentVariables() ?

Since Environment.GetEnvironmentVariables() returns a non-generic IDictionary and it stores key/value pairs in DictionaryEntry, you have to use Seq.cast first:
let dic = Environment.GetEnvironmentVariables()
dic
|> Seq.cast<DictionaryEntry>
|> Seq.filter(fun entry -> entry.Key.ToString().Contains("COMNTOOLS"))
See the relevant docs at https://msdn.microsoft.com/en-us/library/system.collections.idictionary(v=vs.110).aspx. Notice that entry.Key is of type obj, so one has to convert to string before checking string containment.
Instead of using high-order functions, sequence expression might be handy:
let dic = Environment.GetEnvironmentVariables()
seq {
for entry in Seq.cast<DictionaryEntry> dic ->
(string entry.Key), (string entry.Value)
}
|> Seq.filter(fun (k, _) -> k.Contains("COMNTOOLS"))

F# Seq can only operate with System.Collections.Generic.IEnumerable<_>. System.IDictionary returned by Environment.GetEnvironmentVariables is not generic but it implements non-generic System.Collections.IEnumerable and not System.Collections.Generic.IEnumerable<_>. System.Collections.IEnumerable does not contain type information and allows enumeration of the collection of boxed types i.e. instances of System.Object.
Anyway System.IDictionary can be enumerated as collection of System.Collections.DictionaryEntry objects so you can simply call Seq.cast on it. It will give you access to Key and Value properties, yet still boxed as objects, so you should unbox them too.
let dic = System.Environment.GetEnvironmentVariables()
dic
|> Seq.cast<System.Collections.DictionaryEntry>
|> Seq.filter( fun k -> (k.Key :?> string).Contains("COMNTOOLS"))
Alternatively you can use the following function
let asStringPairSeq (d : System.Collections.IDictionary) : seq<string * string> =
Seq.cast<System.Collections.DictionaryEntry> d
|> Seq.map (fun kv -> kv.Key :?> string, kv.Value :?> string)
System.Environment.GetEnvironmentVariables()
|> asStringPairSeq
|> Seq.filter (fun (k,v) -> k.Contains("COMNTOOLS"))

Related

Access DateTime enumerations: Property is not static

I have a sequence of DateTime objects, and I would like to get just the Sundays. .net has the DateTime struct, with a DayOfWeek property. Now, consider the following bit of code:
let sundaysFirstOfMonth = dateRange |> Seq.filter (fun d -> d.DayOfWeek = DateTime.DayOfWeek.Sunday)
This does not compile with a Property 'DayOfWeek' is not static, while this
let sundaysFirstOfMonth = dateRange |> Seq.filter (fun d -> int d.DayOfWeek = 0)
does, as I know that Sunday maps to 0 in the enumeration.
How can I make use of the enumerations without having to cast to int and reference to the int itself?
Thanks!
Found the solution myself. This works:
let sundaysFirstOfMonth = dateRange |> Seq.filter (fun d -> d.DayOfWeek = DayOfWeek.Sunday)
I misunderstood where the enumerations was defined. It is indeed in the System namespace, not in System.DateTime

Extend F# Arrays with lookup for bigint

I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string

F# TryGetValue, can't access the value?

I am using TryGetValue on a Dictionary in F# and it returns an object bool * Dictionary<int, object>
I have been googling for hours but how do I access the bool component of this object so I can check if it has returned a value?
Please save me from going postal...
There are a few options.
The simplest is probably:
let found, value = dict.TryGetValue key
Alternatively:
let pair = dict.TryGetValue key
let found = fst pair
let value = snd pair
The most common pattern is this:
match dict.TryGetValue key with
| true, value -> (* do something with value *)
| _ -> (* handle the case of value not found *)

F# Add New Item to Collections.Map?

I want to create a map and a function to add item to that map.
This is what I did
let mymap = Map.empty
let myfunc nId nValue =
mymap = Map.add nId nValue ;;
But this produced following Error
This expression was expected to have type Map<'a,'b> but here has type Map<'c,'d> -> Map<'c,'d>
What did I do wrong?
Maps are immutable so you need to do let mutable mymap.
Also, = does comparison, you need <- for assignment, which is why you got the error.
Something like
let mutable mymap = Map.empty
let myfunc nId nValue =
mymap <- Map.add nId nValue mymap;;
is what you want
Apart from assigning mymap to a new value as in John's answer, you can do it using a more idiomatic FP way:
let mymap = Map.empty
let myfunc nId nValue =
Map.add nId nValue mymap
Where John's myfunc has a signature of 'a -> 'b -> unit because of the assignment, mine has a signature of 'a -> 'b -> Map<'a, 'b>, which is more common in FP to return a new Map instead of modifying an existing map (mymap).

Converting string array to list of strings [duplicate]

This question already has answers here:
Efficient conversion from String[] to F# List
(3 answers)
Closed 9 years ago.
Is there an easy way to convert a string array into a list of strings in F#? I'm rather new to F# and I can't find anything on this while searching.
You can use Array.toList:
let myArray = [| "foo"; "bar"; "baz" |]
let myList = myArray |> Array.toList
Or for that matter, Seq.toList.
A universal way is with library function from source collection type module Array.toList : 'T [] -> 'T list. It works for any type of elements 'T.
In particular, for 'T being string:
[| "S1"; "S2"; "S3" |] |> Array.toList
Or similarly universally, but with library function from target collection type moduleList.ofArray : 'T [] -> 'T list:
[| "S1"; "S2"; "S3" |] |> List.ofArray

Resources