Reputation: 1453
I'm new to F# and trying to rewrite one of our applications in F# to try and learn it along the way and I am having a bit of trouble flattening a list. I have searched and found several answers, but I can't seem to get any of them to work.
My data type is saying it is val regEntries: RegistryKey list list
I would like it to only be one list.
Below is my code:
namespace DataModule
module RegistryModule =
open Microsoft.Win32
let regEntries =
["SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"]
|> List.map (fun x -> Microsoft.Win32.Registry.LocalMachine.OpenSubKey(x))
|> List.map (fun k ->
List.ofArray (k.GetSubKeyNames())
|> List.map (fun x -> k.OpenSubKey(x))
|> List.filter (fun x -> x.GetValue("ProductId") <> null))
Upvotes: 15
Views: 12294
Reputation: 754763
Try the following
let regEntries =
["SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"]
|> Seq.collect (fun p ->
let k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(p)
k.GetSubKeyNames()
|> Seq.map (fun x -> k.OpenSubKey(x))
|> Seq.filter (fun x -> x.GetValue("ProductId") <> null)))
|> List.ofSeq
The Seq.concat
method is useful for converting a T list list
to a T list
. Note that I switched a lot of your List.
calls to Seq.
calls. There didn't seem to be any need to create an actual list
until the very end hence I kept it as a simple seq
Upvotes: 14
Reputation: 1835
Flattening a list in F#, use the List.collect
function with the id
function as the predicate:
[ [1; 2]; [3; 4; 5]; [6] ] |> List.collect id
returns:
[1; 2; 3; 4; 5; 6]
Works fine for nested lists:
let myList =
[
[ [1; 2]; [3; 4; 5] ];
[ [6]; [] ]
]
myList
|> List.collect id
(* [[1; 2]; [3; 4; 5]; [6]; []] *)
myList
|> List.collect id
|> List.collect id
(* [1; 2; 3; 4; 5; 6] *)
Upvotes: 2
Reputation: 23521
Note that if you found this question looking for a basic way to flatten:
let flatten (source : 'T seq seq) :'T seq =
System.Linq.Enumerable.SelectMany(source, id)
This is a basic call to the .net SelectMany
with the F#'s id
function.
Upvotes: 1
Reputation: 243051
Using the various map
and filter
functions is definitely an option - and it works great (and it is also great way to learn about functional abstractions).
However, you can also use sequence comprehensions, which is a nice syntactic sugar that makes writing these sort of tasks a bit easier (in my opinion). To do the same thing as what is happening in the answer by Jared, you can write:
let subKeys =
[@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"; //"
@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"] // "
let regEntries =
[ for subKey in subKeys do
let k = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(subKey)
for name in k.GetSubKeyNames() do
let x = k.OpenSubKey(name)
if x.GetValue("ProductId") <> null then
yield x ]
The nesting simply becomes nested for
loop and filtering is expressed using if
- to produce a new value of the sequence you can use yield
and the fact that the expression is enclosed in square brackets makes it a list comprehension.
Upvotes: 6