Skip to content

Instantly share code, notes, and snippets.

@roboter
Created May 7, 2021 18:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roboter/c5bb8d2e6e6aa16b492f42373555adfe to your computer and use it in GitHub Desktop.
Save roboter/c5bb8d2e6e6aa16b492f42373555adfe to your computer and use it in GitHub Desktop.
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
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