F# - Remove duplicate characters after first in string

我们两清 提交于 2019-12-22 08:23:34

问题


What I am trying to do is to remove duplicates of a specific given char in a string but letting the first char to remain. I.e:

let myStr = "hi. my .name."

//a function that gets a string and the element to be removed in the string
someFunc myStr "."  

where someFunc returns the string showen as below:

"hi. my name"

It is easy to remove duplicates from a string, but is there a way to remove the duplicates but letting the first duplicated element remain in the string?


回答1:


Here's one approach:

let keepFirst c s =
    Seq.mapFold (fun k c' -> (c', k||c<>c'), k&&c<>c') true s
    |> fst
    |> Seq.filter snd
    |> Seq.map fst
    |> Array.ofSeq
    |> System.String

let example = keepFirst '.' "hi. my .name."



回答2:


let someFunc (str : string) c =
    let parts = str.Split([| c |])
    if Array.length parts > 1 then
        seq {
            yield Array.head parts
            yield string c
            yield! Array.tail parts
        }
        |> String.concat ""
    else
        str

Note that the character is given as char instead of a string.




回答3:


let someFunc chr (str:string) =
    let rec loop (a: char list) b = function
        | [] -> a |> List.rev |> System.String.Concat
        | h::t when h = chr -> if b then loop a b t 
                               else loop (h::a) true t
        | h::t -> loop (h::a) b t
    loop [] false (str.ToCharArray() |> Array.toList)

Note that the character is given as char instead of a string.

Edit: Another way would be using regular expressions

open System.Text.RegularExpressions

let someOtherFunc c s =
    let pat = Regex.Escape(c)
    Regex.Replace(s, sprintf "(?<=%s.*)%s" pat pat, "")

Note that, in this case the character is given as string.

Edit 2:

let oneMoreFunc (c:char) (s:string) =
    let pred = (<>) c
    [ s |> Seq.takeWhile pred
      seq [c]
      s |> Seq.skipWhile pred |> Seq.filter pred ]
    |> Seq.concat
    |> System.String.Concat



回答4:


When devising a function, think about gains from making its arguments generic. To pass state through the iteration, barring mutable variables, Seq.scan could be a weapon of choice. It folds into a tuple of new state and an option, then Seq.choose strips out the state and the unwanted elements.

In terms of functional building blocks, make it accept a predicate function 'a -> bool and let it return a function seq<'a> -> seq<'a>.

let filterDuplicates predicate =
    Seq.scan (fun (flag, _) x ->
        let p = predicate x in flag || p,
        if flag && p then None else Some x ) (false, None)
    >> Seq.choose snd

This can then easily reused to do other things as well, like 0 together with odd numbers.

filterDuplicates (fun i -> i % 2 = 0) [0..10]
// val it : seq<int> = seq [0; 1; 3; 5; ...]

Supplied with a call to the equality operator and fed into the constructor of System.String, you'll get near the signature you want, char -> seq<char> -> System.String.

let filterDuplicatesOfChar what s = 
    System.String(Array.ofSeq <| filterDuplicates ((=) what) s)
filterDuplicatesOfChar '.' "hi. my .name."
// val it : string = "hi. my name"


来源:https://stackoverflow.com/questions/42485552/f-remove-duplicate-characters-after-first-in-string

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!