Get a list of invalid drive letters - f#

let private GetDrives = seq{
let all=System.IO.DriveInfo.GetDrives()
for d in all do
//if(d.IsReady && d.DriveType=System.IO.DriveType.Fixed) then
yield d
}
let valid={'A'..'Z'}
let rec SearchRegistryForInvalidDrive (start:RegistryKey) = seq{
let validDrives=GetDrives |> Seq.map (fun x -> x.Name.Substring(0,1))
let invalidDrives= Seq.toList validDrives |> List.filter(fun x-> not (List.exists2 x b)) //(List.exists is the wrong method I think, but it doesn't compile
I followed F#: Filter items found in one list from another list but could not apply it to my problem as both the solutions I see don't seem to compile. List.Contains doesn't exist (missing a reference?) and ListA - ListB doesn't compile either.

open System.IO
let driveLetters = set [ for d in DriveInfo.GetDrives() -> d.Name.[0] ]
let unused = set ['A'..'Z'] - driveLetters

Your first error is mixing between char and string, it is good to start with char:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0])
Now invalid drive letters are those letters which are in all but not in validDrives:
let invalidDrives =
all |> Seq.filter (fun c -> validDrives |> List.forall ((<>) c))
Since validDrives is traversed many times to check for membership, turning it to a set is better in this example:
let all = {'A'..'Z'}
let validDrives = GetDrives |> Seq.map (fun x -> x.Name.[0]) |> Set.ofSeq
let invalidDrives = all |> Seq.filter (not << validDrives.Contains)

Related

Print list of document in reverse line by line

So I am working on this assignment where I have to reverse the document but it does not work as intended. If I run my code on a file like this
hello
world
I will get
dlrow
olleh
But what I really need is the following:
world
hello
So I need to reverse it line by line but not for every letter. I dont know how to tackle this problem so I need someone who can push me in the right direction. I think that I need to do something with my "q"
My Code is:
let readFile (filename : string) : string option =
try
let reader = System.IO.File.OpenText filename
Some (reader.ReadToEnd ())
with _ -> None
let tac (filenames : string list) : string option =
try
let q = List.map readFile filenames |> List.choose id |> String.concat ", " |> Seq.toList |> List.rev
(Some (System.String.Concat(Array.ofList (q))))
with _ -> None
As Daniel said, ...divide and conquer
Break it into smaller steps and solve one at a time
When you know what it takes to solve it,
you can then choose to improve it, 'refactor' it
to optimize
or add error handling
or specialize/rewrite a function
...etc
open System
open System.IO
// V1
File.ReadLines "./my.txt"
|> Seq.toList
|> List.rev
|> List.map (fun x -> sprintf "%s%s" x Environment.NewLine)
|> List.fold (+) ""
|> printf "%A\n"
... how do you reverse it without a for loop ?
open System
open System.IO
// v2
let rec reverse =
function
| [] -> []
| h::t -> (reverse t)#[h]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
// look , I'm a 'compositor' ! :) ♪
let chain = reverse >> List.map newLine >> join
File.ReadLines "./my.txt"
|> Seq.toList
|> chain
|> printf "%A\n"
... what if it doesn't exists ?
// V3
open System
open System.IO
let rec reverse =
function
| [] -> []
| h::t -> (reverse t)#[h]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
// 👣
let chain = reverse >> List.map newLine >> join
// NEW
let readLines path =
if File.Exists path then
// 🐛: What if its Binary or something line that ?
File.ReadLines path |> Seq.toList
else
[]
readLines "./my.txt"
|> chain
|> printf "%A\n"
[Edit]
Optional string version
// v4
open System
open System.IO
// New
let inline maybe test f x =
if test x then x |> f |> Some else None
// New
let inline ifSome f = maybe Option.isSome f
// New
let inline ifNone f = maybe Option.isNone f
// New
let ifExists = maybe File.Exists
let rec reverse =
function
| [] -> []
| h :: t -> (reverse t) # [ h ]
let join = List.fold (+) ""
let newLine x = sprintf "%s%s" x Environment.NewLine
let chain =
reverse
>> List.map newLine
>> join
let readLines path =
(File.ReadLines path)
|> Seq.toList
// usage '$ fsi reverse.fsx "my.txt"'
fsi.CommandLineArgs
|> Seq.skip 1
|> Seq.head // expecting "my.txt"
|> ifExists readLines
|> Option.map chain
|> ifSome (fun x -> printf "%A\n" x)
|> ifNone (fun x -> printf "None! \n")

F# find all elements present in another array

this solution
d1 |> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
from this so answer
Finding the difference between two arrays in FSharp
gives this error
Severity Code Description Project File Line Suppression State
Error Type mismatch. Expecting a
unit -> bool
but given a
'a [] -> bool
The type 'unit' does not match the type ''a []' ParseLibs
Program.fs 25
Full code:
// Learn more about F# at http://fsharp.org
// See the 'F# Tutorial' project for more help.
open System
open System.IO
open FSharp.Collections
[<EntryPoint>]
let main argv =
let path = "data1.txt"
let lines = use reader = new StreamReader(path) in reader.ReadToEnd().Split('\n')
let n = 5
let d1 = lines
|> Array.takeWhile (fun e -> not (e.Equals "\r"))
let d2 = lines
|> Array.skipWhile (fun e -> not (e.Equals "\r"))
|> Array.skip 1
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
|> Array.iter (fun (e, i) -> printfn "%s" e)
d1 |> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
//let writer = new StreamWriter(path)
ignore (Console.ReadKey())
0 // return an integer exit code
Is the answer there wrong? What is the real answer? I am simply trying to filter all the elements that are in both arrays. In most functional languages this is as trivial as they come.
d1 |> Array.filter (fun t -> d2.contains(t))
The problem is that d2 has type unit.
As array.iter returns ()
I would change to
let d2 = lines
|> Array.skipWhile (fun e -> not (e.Equals "\r"))
|> Array.skip 1
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
d1
|> Array.filter (fun t -> d2 |> Array.exists (fun t2 -> t=t2))
|> Array.iter (fun (e, i) -> printfn "%s" e)
Using the actual answer from the above link: https://stackoverflow.com/a/28682277/5514938 and adding the information at https://en.wikipedia.org/wiki/Set_theory#Basic_concepts_and_notation
the following code is an example of the three first concepts with same sets/values as in wiki article.
let d1= [|"1";"2";"3";|] //pretend this to be the filtered/skipped/mapped
//whatever but otherwise "clean" structure/datatypes
let d2 = [|"2";"3";"4";|] //pretend this to be the filtered/skipped/mapped
//whatever but otherwise "clean" structure/datatypes
//equal to d1
let s1 = d1
|> Set.ofArray
let s2 = d2
|> Set.ofArray
let all = s1 + s2 //Union
let inBoth = Set.intersect s1 s2 //Intersection
let onlyIn_d1 = s1 - s2 //Set difference
let onlyIn_d2 = s2 - s1 //Set difference the other way ;-)
I have removed your other code to simplify the concepts, so the initial filter, skippings and mappings you are doing must of course be done ahead of the above code. And you must also "realign" the types to be equal again:
|> Array.mapi (fun i e -> e, i)
|> Array.filter (fun (e, i) -> i % n = 0)
|> Array.map (fun (e,i) -> e)
|> Set.ofArray

Converting None to 0 (F#)

This expression correctly evaluates to 225.58:
let alfki = ctx.``[dbo].[Customers]``.Individuals.ALFKI
let changeNoneToZero x = match x with | Some v -> v | None -> 0m
let freights = alfki.FK_Orders_Customers |> Seq.map (fun x -> changeNoneToZero x.Freight) |> Seq.sum
corresponding to the following database query:
select SUM(freight)
from dbo.Orders
where CustomerID = (
select CustomerID from Customers
where CustomerID = 'ALFKI')
Is there some built in equivalent or close analogue of the changeNoneToZero function? Could I be seeking to Seq.sumBy something instead?
#ildjarn's approach of using Seq.choose works. Instead of converting None to 0m and then summing everything, the following selects only those values that are not None and pipes them into Seq.sum:
let freights = alfki.FK_Orders_Customers
|> Seq.choose (fun x -> x.Freight)
|> Seq.sum
Thanks #ildjarn!
You can also use Option.fold:
alfki.FK_Orders_Customers |> Option.fold (fun _ a -> a) 0m
And you can make it generic:
let inline valOrZero ov = ov |> Option.fold (fun _ a -> a) LanguagePrimitives.GenericZero

Find all strings which has sub-string of an item in another string list

I have the following code.
let s1 = [(12, "abcde12345"); (23, "bcdef2345"); (12, "xyzafg3838")]
let s2 = ["bcd"; "345"]
What's the best way to find all items in s1 which second item has sub-string of any one in s2?
(12, "abcde12345"); (23, "bcdef2345")
In my real code s1 is a Seq.
Seq.filter (fun (_, x) -> List.exists (x.Contains) s2) s1
I figured out one.
s1 |> Seq.filter (fun i -> List.exists (fun e -> (snd i).Contains(e)) s2)
Concat all of the items from the second set into a regular expression, then apply it on each item in the first set.
open System
open System.Text.RegularExpressions
let setA = [ "One"; "Two"; "Three" ]
let setB = [ "o"; "n" ];
let pattern = String.Join("|", setB);
let regex = new Regex(pattern);
let results = setA |> List.filter (fun str -> regex.Match(str).Success)
results |> List.iter (fun result -> Console.WriteLine(result))

f# array.filter based on a bool array

if I have array A, and I have another bool array isChosen with the same length of A how can I build a new array from A where isChosen is true? something like A.[isChosen]? I cannot use Array.filter directly since isChosen is not a function of A elements and there is no Array.filteri like Array.mapi.
zip should help:
let l = [|1;2;3|]
let f = [|true; false; true|]
let r = [| for (v, f) in Seq.zip l f do if f then yield v|]
// or
let r = (l, f) ||> Seq.zip |> Seq.filter snd |> Seq.map fst |> Seq.toArray
Try the zip operator
seq.zip A isChosen
|> Seq.filter snd
|> Seq.map fst
|> Array.ofSeq
This will create a sequence of tuples where one value is from A and the other is from isChosen. This will pair the values together and make it very easy to filter them out in a Seq.filter expression
It's not as elegant or 'functional' as the other answers, but every once in a while I like a gentle reminder that you can use loops and array indices in F#:
let A = [|1;2;3|]
let isChosen = [|true; false; true|]
let r = [| for i in 0..A.Length-1 do
if isChosen.[i] then
yield A.[i] |]
printfn "%A" r
:)
And here are two more ways, just to demonstrate (even) more F# library functions:
let A = [|1;2;3|]
let isChosen = [|true;false;true|]
let B = Seq.map2 (fun x b -> if b then Some x else None) A isChosen
|> Seq.choose id
|> Seq.toArray
let C = Array.foldBack2 (fun x b acc -> if b then x::acc else acc) A isChosen []
|> List.toArray
My personal favorite for understandability (and therefore maintainability): desco's answer
let r = [| for (v, f) in Seq.zip l f do if f then yield v|]

Resources