How convert first char to lowerCase - string

Try to play with string and I have string like: "Hello.Word" or "stackOver.Flow"
and i what first char convert to lower case: "hello.word" and "stackOver.flow"
For snakeCase it easy we need only change UpperCase to lower and add '_'
but in camelCase (with firs char in lower case) i dont know how to do this
open System
let convertToSnakeCase (value:string) =
String [|
Char.ToLower value.[0]
for ch in value.[1..] do
if Char.IsUpper ch then '_'
Char.ToLower ch |]
Who can help?
module Identifier =
open System
let changeCase (str : string) =
if String.IsNullOrEmpty(str) then str
else
let isUpper = Char.IsUpper
let n = str.Length
let builder = new System.Text.StringBuilder()
let append (s:string) = builder.Append(s) |> ignore
let rec loop i j =
let k =
if i = n (isUpper str.[i] && (not (isUpper str.[i - 1])
((i + 1) <> n && not (isUpper str.[i + 1]))))
then
if j = 0 then
append (str.Substring(j, i - j).ToLower())
elif (i - j) > 2 then
append (str.Substring(j, 1))
append (str.Substring(j + 1, i - j - 1).ToLower())
else
append (str.Substring(j, i - j))
i
else
j
if i = n then builder.ToString()
else loop (i + 1) k
loop 1 0
type System.String with
member x.ToCamelCase() = changeCase x
printfn "%s" ("StackOver.Flow".ToCamelCase()) //stackOver.Flow
//need stackOver.flow

I suspect there are much more elegant and concise solutions, I sense you are learning functional programming, so I think its best to do stuff like this with recursive function rather than use some magic library function. I notice in your question you ARE using a recusive function, but also an index into an array, lists and recursive function work much more easily than arrays, so if you use recursion the solution is usually simpler if its a list.
I'd also avoid using a string builder, assuming you are learning fp, string builders are imperative, and whilst they obviously work, they wont help you get your head around using immutable data.
The key then is to use the pattern match to match the scenario that you want to use to trigger the upper/lower case logic, as it depends on 2 consecutive characters.
I THINK you want this to happen for the 1st char, and after a '.'?
(I've inserted a '.' as the 1st char to allow the recursive function to just process the '.' scenario, rather than making a special case).
let convertToCamelCase (value : string) =
let rec convertListToCamelCase (value : char list) =
match value with
| [] -> []
| '.' :: second :: rest ->
'.' :: convertListToCamelCase (Char.ToLower second :: rest)
| c :: rest ->
c :: convertListToCamelCase rest
// put a '.' on the front to simplify the logic (and take it off after)
let convertAsList = convertListToCamelCase ('.' :: (value.ToCharArray() |> Array.toList))
String ((convertAsList |> List.toArray).[1..])
The piece to worry about is the recusive piece, the rest of it is just flipping an array to a list and back again.

Related

F# Count how Many times a substring Contains within a string

How could one count how many times a substring exists within a string?
I mean if you have a String "one, two, three, one, one, two" how could you make it count "one" being present 3 times?
I thought String.Contains would be able to do the job but that only checks if the substring is present at all. String.forall is for chars and therefofre niether an option.
So i am really at a complete halt here. Can some enligten me?
You can use Regex.Escape to turn the string you're searching for into a regex, then use regex functions:
open System.Text.RegularExpressions
let countMatches wordToMatch (input : string) =
Regex.Matches(input, Regex.Escape wordToMatch).Count
Test:
countMatches "one" "one, two, three, one, one, two"
// Output: 3
Here's a simple implementation that walks through the string, using String.IndexOf to skip through to the next occurrence of the substring, and counts up how many times it succeeds.
let substringCount (needle : string) (haystack : string) =
let rec loop count (index : int) =
if index >= String.length haystack then count
else
match haystack.IndexOf(needle, index) with
| -1 -> count
| idx -> loop (count + 1) (idx + 1)
if String.length needle = 0 then 0 else loop 0 0
Bear in mind, this counts overlapping occurrences, e.g., subtringCount "aa" "aaaa" = 3. If you want non-overlapping, simply replace idx + 1 with idx + String.length needle.
Create a sequence of tails of the string to search in, that is, all substring slices anchored at its end. Then you can use forall functionality to determine the number of matches against the beginning of each of them. It's just golfier than (fun s -> s.StartsWith needle).
let count needle haystack =
[ for i in 0..String.length haystack - 1 -> haystack.[i..] ]
|> Seq.filter (Seq.forall2 (=) needle)
|> Seq.length
count "aba" "abacababac"
// val it : int = 3
a fellow student of mine came up with the so far simpelst solutions i have seen.
let countNeedle (haystack :string) (needle : string) =
match needle with
| "" -> 0
| _ -> (haystack.Length - haystack.Replace(needle, "").Length) / needle.Length
// This approach assumes the data is comma-delimited.
let data = "one, two, three, one, one, two"
let dataArray = data.Split([|','|]) |> Array.map (fun x -> x.Trim())
let countSubstrings searchTerm = dataArray |> Array.filter (fun x -> x = searchTerm) |> Array.length
let countOnes = countSubstrings "one"
let data' = "onetwothreeoneonetwoababa"
// This recursive approach makes no assumptions about a delimiter,
// and it will count overlapping occurrences (e.g., "aba" twice in "ababa").
// This is similar to Jake Lishman's answer.
let rec countSubstringFromI s i what =
let len = String.length what
if i + len - 1 >= String.length s then 0
else (if s.Substring(i, len) = what then 1 else 0) + countSubstringFromI s (i + 1) what
let countSubStrings' = countSubstringFromI data' 0 "one"

String Matching with newline character in Haskell

Here I am trying to find the index of '-' followed by '}' in a String.
For an input like sustringIndex "abcd -} sad" it gives me an output of 10
which is giving me the entire string length.
Also if I do something like sustringIndex "abcd\n -} sad" it gives me 6
Why is that so with \n. What am I doing wrong. Please correct me I'm a noob.
substrIndex :: String -> Int
substrIndex ""=0
substrIndex (s:"") = 0
substrIndex (s:t:str)
| s== '-' && t == '}' = 0
| otherwise = 2+(substrIndex str)
Your program has a bug. You are checking every two characters. But, what if the - and } are in different pairs, for example S-}?
It will first check S and - are equal to - and } respectively.
Since they don't match, it will move on with } alone.
So, you just need to change the logic a little bit, like this
substrIndex (s:t:str)
| s == '-' && t == '}' = 0
| otherwise = 1 + (substrIndex (t:str))
Now, if the current pair doesn't match -}, then just skip the first character and proceed with the second character, substrIndex (t:str). So, if S- doesn't match, your program will proceed with -}. Since we dropped only one character we add only 1, instead of 2.
This can be shortened and written clearly, as suggested by user2407038, like this
substrIndex :: String -> Int
substrIndex [] = 0
substrIndex ('-':'}':_) = 0
substrIndex (_:xs) = 1 + substrIndex xs

Haskell recursive nextWord function

I wanted to implement a function nextWord which given a list of characters s (i.e. a String)
returns a pair consisiting of the next word in s and the remaining characters in s after the
first word has been removed. I tried this and I got error messages:
nextWord :: String -> (String, String)
nextWord [] = ([],[])
nextWord (next:rest)
| isSpace next = ([],rest)
| otherwise = next : [restWord,restString]
where
(restWord,restString) = nextWord next
However, when I looked at the solution I noticed that there is a big resemblance:
nextWord :: String -> ( String, String )
nextWord [] = ( [], [] )
nextWord ( next : rest )
| isSpace next = ([], rest)
| otherwise = let (restWord, restString) = nextWord rest
in (next : restWord, restString)
which works perfectly.
My question is first of all, why didn't my function work? I know I'm doing something wrong when defining restWord / restString.
Also, in the solution, how does the last line work?
next : restWord , restString
restWord, restString isn't a list, therefore how does the cont work here?
Thanks!
In the function you wrote, you sometimes return ([],rest), a tuple, and other times return next : [restWord,restString], a list. Those two types are incompatible, and so the function can't be typed.
In the working function, (next : restWord, restString) is a tuple: its fst is next:restWord and its snd is restString. This way, the function's return value is always a tuple, so the types line up.

split string logic error

Hello this function should take a String and return a list of Strings split at the Char c. I should define some helper functions but currently the user must initialize args that should be hidden from them.
xs = output list, i = start index for substr, j = end index for substr
example: split "123,456,789" ',' [] 0 0
should yield ["789", "456", "123"]
split s c xs i j =
if j == length s
then (subStr s i j) : xs
else if head (drop j s) == c
then split s c (subStr s i j : xs) (j + 1) (j + 1)
else split s c xs i (j + 1)
subStr s i j = take j(drop i s)
When i apply the function with the following args: split "123,456,789" ',' [] 0 0
I'm getting the result: ["789", "456,789", "123"]
I already mentioned this on your other post, but the issue with this is your subStr function. If you change it to subStr s i j = take (j-i) (drop i s) it should work. And if that's all you want, great. But it could be written more clearly and easily using takeWhile, or using split from data.Text.
Also, type signatures please. (Although I do appreciate that you defined the inputs this time.) Not only do they make it easier for us to help, you can often solve your own problems in the process of figuring them out.

How can I extract the middle part of a string in FSharp?

I want to extract the middle part of a string using FSharp if it is quoted, similar like this:
let middle =
match original with
| "\"" + mid + "\"" -> mid
| all -> all
But it doesn't work because of the infix operator + in pattern expression. How can I extract this?
I don't think there is any direct support for this, but you can certainly write an active pattern. Active patterns allow you to implement your own code that will run as part of the pattern matching and you can extract & return some part of the value.
The following is a pattern that takes two parameters (prefix and postfix string) and succeeds if the given input starts/ends with the specified strings. The pattern is not complete (can fail), so we'll use the |Name|_| syntax and it will need to return option value:
let (|Middle|_|) prefix postfix (input:string) =
// Check if the string starts with 'prefix', ends with 'postfix' and
// is longer than the two (meaning that it contains some middle part)
if input.StartsWith(prefix) && input.EndsWith(postfix) &&
input.Length >= (prefix.Length + postfix.Length) then
// Strip the prefix/postfix and return 'Some' to indicate success
let len = input.Length - prefix.Length - postfix.Length
Some(input.Substring(prefix.Length, len))
else None // Return 'None' - string doesn't match the pattern
Now we can use Middle in pattern matching (e.g. when using match):
match "[aaa]" with
| Middle "[" "]" mid -> mid
| all -> all
Parameterized active patterns to the rescue!
let (|HasPrefixSuffix|_|) (pre:string, suf:string) (s:string) =
if s.StartsWith(pre) then
let rest = s.Substring(pre.Length)
if rest.EndsWith(suf) then
Some(rest.Substring(0, rest.Length - suf.Length))
else
None
else
None
let Test s =
match s with
| HasPrefixSuffix("\"","\"") inside ->
printfn "quoted, inside is: %s" inside
| _ -> printfn "not quoted: %s" s
Test "\"Wow!\""
Test "boring"
… or just use plain old regular expression
let Middle input =
let capture = Regex.Match(input, "\"([^\"]+)\"")
match capture.Groups.Count with
| 2 -> capture.Groups.[1].Value
| _ -> input
Patterns have a limited grammar - you can't just use any expression. In this case, I'd just use an if/then/else:
let middle (s:string) =
if s.[0] = '"' && s.[s.Length - 1] = '"' && s.Length >= 2 then
s.Substring(1,s.Length - 2)
else s
If grabbing the middle of a string with statically known beginnings and endings is something that you'll do a lot, then you can always use an active pattern as Tomas suggests.
Not sure how efficient this is:
let GetQuote (s:String) (q:char) =
s
|> Seq.skip ((s |> Seq.findIndex (fun c -> c = q))+1)
|> Seq.takeWhile (fun c-> c <> q)
|> Seq.fold(fun acc c -> String.Format("{0}{1}", acc, c)) ""
Or there's this with Substring in place of the fold:
let GetQuote2 (s:String) (q:char) =
let isQuote = (fun c -> c = q)
let a = (s |> Seq.findIndex isQuote)+1
let b = ((s |> Seq.take(a) |> Seq.findIndex isQuote)-1)
s.Substring(a,b);
These will get the first instance of the quoted text anywhere in the string e.g "Hello [World]" -> "World"

Categories

Resources