Created
May 7, 2021 18:00
-
-
Save roboter/c5bb8d2e6e6aa16b492f42373555adfe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace ClassFinder.Lib | |
open System | |
module ClassFinder = | |
Console.ReadLine() |> System.Int32.Parse |> fun x-> [1..x] |> List.iter (fun _ -> printfn "Hello World") | |
let isUpper c = | |
c |> Char.IsUpper | |
let toUpper c = | |
c |> Char.ToUpper | |
let toString c = | |
c |> Char.ToString | |
let getClassName (classNameWithNamespace:string) = | |
classNameWithNamespace.Split('.') |> Seq.last | |
let trim (s:string) = | |
s.Trim() | |
let notEmpty s = s|> String.IsNullOrEmpty |> not | |
let length (s:string) = s.Length | |
let endsWithSpace s = s |> Seq.last = ' ' | |
let splitByCase s = | |
let mutable res = "" | |
let index = length s - 1 | |
[for i in 0..index do | |
if isUpper s.[i] then | |
if i > 0 then yield res | |
res <- toString s.[i] | |
if i = index then yield res | |
else | |
res <- res + toString s.[i] | |
if i = index then yield res | |
] | |
let compareChar (a, b) = | |
a = b || b ='*' | |
let toChar s searchIndex = | |
s |> Seq.take searchIndex | |
let paramsBigger a b = | |
length a > length b | |
let matchClass name search = | |
let timmedSearch = trim search | |
let len = length timmedSearch | |
if paramsBigger timmedSearch name then false | |
else | |
Seq.zip | |
(toChar name len) | |
(toChar timmedSearch len) | |
|> Seq.forall compareChar | |
let find list s = | |
try list |> List.findIndex (fun x -> matchClass x s) | |
with | |
| _ -> -1 | |
let patternsCountBiggerThanClasses (list: List<string>) (pattern: List<string>) = | |
pattern.Length > list.Length | |
let allResultsAreTrue x = x |> List.forall(fun x -> x) | |
let matchClassList list pattern = | |
let mutable l = (list:List<string>) | |
[for i in pattern do | |
let last = endsWithSpace i | |
let index = find l i | |
l <- l.[(index+1)..] | |
if last && (l.Length > 0) then false | |
if index = -1 then false | |
else true | |
] |> allResultsAreTrue | |
let containsUpper s = | |
[for c in s->c] |> List.exists isUpper | |
let convertToUpper s = | |
List.ofSeq s | |
|> List.map toUpper | |
|> List.toArray | |
|> System.String | |
let changeToUpperCaseIfNeeded s = | |
if containsUpper s | |
then s | |
else convertToUpper s | |
let filter className pattern = | |
let splittedClassName = className |> getClassName |> trim |> splitByCase | |
let splittedPattern = changeToUpperCaseIfNeeded pattern |> splitByCase | |
if patternsCountBiggerThanClasses splittedClassName splittedPattern then false | |
else matchClassList splittedClassName splittedPattern | |
let findInAList list pattern = | |
list | |
|> List.filter notEmpty | |
|> List.filter (fun s -> filter s pattern) | |
|> List.sortBy getClassName | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ``ClassFinder Tests`` | |
open Xunit | |
open ClassFinder.Lib | |
open Microsoft.FSharp.Reflection | |
open System.Collections.Generic | |
[<Theory>] | |
[<InlineData("c.d.FooBar","FooBar")>] | |
[<InlineData("c.d.FooBarBaz","FooBarBaz")>] | |
[<InlineData("FooBarBaz","FooBarBaz")>] | |
[<InlineData("a.a.FooBarBaz.","")>] | |
let ``Get Class Name`` (fullname, expected)= | |
Assert.Equal(expected, ClassFinder.getClassName fullname) | |
type SplitClassTestData() = | |
static member Data = | |
[ ("FooBarBaz", ["Foo"; "Bar"; "Baz"]) | |
("FooBarzoo", ["Foo"; "Barzoo"]) | |
("YourEyesAreSpinningInTheirSockets", ["Your"; "Eyes"; "Are"; "Spinning"; "In"; "Their"; "Sockets"]) | |
("FUN", ["F"; "U"; "N"]) | |
("FoBa", ["Fo"; "Ba"]) | |
("", []) | |
] |> Seq.map FSharpValue.GetTupleFields | |
[<Theory; MemberData("Data", MemberType=typeof<SplitClassTestData>)>] | |
let ``All CasesTest`` (input, expected) = | |
Assert.Equal<IEnumerable<string>>(expected, ClassFinder.splitByCase input) | |
[<Theory>] | |
[<InlineData("Foo", "F", true)>] | |
[<InlineData("Foo", "f", false)>] | |
[<InlineData("Foo", "Foo", true)>] | |
[<InlineData("Bar", "Ba", true)>] | |
[<InlineData("Baz", "F", false)>] | |
[<InlineData("Baz", "Fa", false)>] | |
[<InlineData("Baz", "Fa", false)>] | |
[<InlineData("Foo", "Fo0o", false)>] //if pattern > than class | |
//The search pattern may include wildcard characters `'*'` which match missing letters | |
[<InlineData("Foo", "F***", false)>] //if pattern > than class | |
[<InlineData("Bar", "B*r", true)>] | |
[<InlineData("Foo", "F**", true)>] | |
[<InlineData("Foo", "Foo ", true)>] | |
let ``Match Class`` (className, pattern, expected)= | |
Assert.Equal(expected, ClassFinder.matchClass className pattern) | |
[<Theory>] | |
[<InlineData("Foo","Foo")>] | |
[<InlineData("fbb","FBB")>] | |
[<InlineData("fO","fO")>] | |
let ``If the search pattern consists of only lower case characters then the search becomes case insensitive`` (finder, expected) = | |
Assert.Equal(expected, ClassFinder.changeToUpperCaseIfNeeded finder) | |
[<Theory>] | |
[<InlineData("Foo ",true)>] | |
[<InlineData("fbb",false)>] | |
[<InlineData("fO ",true)>] | |
let ``String ends with space`` (finder, expected) = | |
Assert.Equal(expected, ClassFinder.endsWithSpace finder) | |
type FindClassTestData() = | |
static member MatchData = | |
[ ( ["Foo"; "Bar"; "Baz"], ["Foo"], true) | |
(["Foo"; "Barzoo"], ["F"], true) | |
(["Your"; "Eyes"; "Are"; "Spinning"; "In"; "Their"; "Sockets"], ["Y"; "E"; "A"; "S"; "I"; "T"; "S"], true) | |
(["F"; "U"; "N"], ["F"; "U"; "N"], true) | |
( ["Fo"; "Ba"],["Fo"; "Ba"], true) | |
([], [], true) | |
] |> Seq.map FSharpValue.GetTupleFields | |
[<Theory; MemberData("MatchData", MemberType=typeof<FindClassTestData>)>] | |
let ``find a match`` (clasesNames, pattern, expected) = | |
Assert.Equal(expected, ClassFinder.matchClassList clasesNames pattern) | |
let all =[ | |
"a.b.FooBarBaz"; | |
"c.d.FooBar"; | |
""; | |
"codeborne.WishMaker"; | |
"codeborne.MindReader"; | |
"TelephoneOperator"; | |
"ScubaArgentineOperator"; | |
" YoureLeavingUsHere"; | |
" YouveComeToThisPoint"; | |
"YourEyesAreSpinningInTheirSockets"] | |
type FindClassAllTestData() = | |
static member AllDataFromFile = | |
[ | |
(all, "F", ["c.d.FooBar"; "a.b.FooBarBaz"]) | |
(all, "YEASITS", ["YourEyesAreSpinningInTheirSockets"]) | |
(all, "FUN", []) | |
(all,"FoBa", ["c.d.FooBar"; "a.b.FooBarBaz"]) | |
// The found class names must be sorted in alphabetical order ignoring package names | |
// (package names must still be included in the output). | |
(all, "", [ | |
" YoureLeavingUsHere"; | |
" YouveComeToThisPoint"; | |
"c.d.FooBar"; | |
"a.b.FooBarBaz"; | |
"codeborne.MindReader"; | |
"ScubaArgentineOperator"; | |
"TelephoneOperator"; | |
"codeborne.WishMaker"; | |
"YourEyesAreSpinningInTheirSockets"]) | |
(all, "Foo", ["c.d.FooBar"; "a.b.FooBarBaz"]) | |
(["c.c.FooC"; "a.b.FooA"; "c.d.FooB"], "Foo", ["a.b.FooA"; "c.d.FooB"; "c.c.FooC"]) | |
(["c.c.FooC"; "a.b.FooA"; "c.d.FooB"], "FooA", ["a.b.FooA"]) | |
// Upper case letters written in the wrong order will not find any results, for example | |
// `'BF'` will not find `c.d.FooBar`. | |
(["aa.bb.ccc.FooBarBiz"], "BF", []) | |
// If the search pattern consists of only lower case characters then the search becomes | |
// case insensitive (`'fbb'` finds `FooBarBaz` but `'fBb'` will not). | |
(["aa.bb.ccc.FooBarBiz"], "fbb", ["aa.bb.ccc.FooBarBiz"]) | |
(["aa.bb.ccc.FooBarBiz"], "fBb", []) | |
// If the search pattern ends with a space `' '` then the last word in the pattern must | |
// also be the last word of the found class name (`'FBar '` finds `FooBar` and `FooBarzoo` but not `FooBarBaz`). | |
(["FooBar"; "FooBarzoo"; "FooBarBaz"], "FBar ", ["FooBar"; "FooBarzoo"]) | |
// The search pattern may include wildcard characters `'*'` which match missing letters | |
// (`'B*rBaz'` finds `FooBarBaz`i but `BrBaz` does not). | |
(["FooBar"; "FooBarzoo"; "FooBarBaz"; "BrBaz"], "B*rBaz", ["FooBarBaz"]) | |
] |> Seq.map FSharpValue.GetTupleFields | |
[<Theory; MemberData("AllDataFromFile", MemberType=typeof<FindClassAllTestData>)>] | |
let ``All classes from a file Sored by Class Name`` (className, pattern, expected) = | |
Assert.Equal<IEnumerable<string>>(expected, ClassFinder.findInAList className pattern) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment