0

I have a string of 11223.3445566.7788.9900 now I want to create a function where I can remove the duplicate of the specific character in that string and leave only the first in the left.

This is my example code on how for better explanation

let string = "11223.3445566.7788.9900"

let removedDuplicate = removeDuplicate(string: string, char: ".")

print(removedDuplicate) // result 11223.344556677889900

You see, Unlike here I only need to remove the duplicate of the specific character in the string, not all duplicated characters.

Dylan
  • 1,121
  • 1
  • 13
  • 28

2 Answers2

2

You can find the position of the first occurrence of the specific character and then remove all other occurrences after that position:

func removeDuplicate(string: String, char: Character) -> String {
    if var idx = string.firstIndex(of: char) {
        string.formIndex(after: &idx)
        var s = string
        s.replaceSubrange(idx..., with: s[idx...].filter { $0 != char })
        return s
    } else {
        return string
    }
}

Example:

let string = "11223.3445566.7788.9900"
print(removeDuplicate(string: string, char: "."))
// 11223.344556677889900
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • or simply `return string[...idx] + string[idx...].filter{$0 != char}` – Leo Dabus Sep 16 '20 at 07:36
  • @LeoDabus: Yes, that is another option. I just wonder why the substring concatenation returns a `String` and not a `SubString` ... – Martin R Sep 16 '20 at 07:41
  • 1
    @LeoDabus: Now I see it: `string[idx...].filter()` returns a `String`, and there is an overload of `+` taking a sequence as first argument, a String as second argument, and returning a String – Martin R Sep 16 '20 at 07:45
  • When extending StringProtocol If I constrain it to RangeReplaceableCollection it does allow me to concatenate two subsequences `static func + (lhs: Self.SubSequence, rhs: Other) -> Self.SubSequence where Other : RangeReplaceableCollection, Self.Element == Other.Element` – Leo Dabus Sep 16 '20 at 07:56
  • Funny thing when extending StringProtocol and constraining it to RangeReplaceableCollection it returns a subsequence even when using filter. Filter returns Self so if you apply it to a subsequence it returns a subsequence. – Leo Dabus Sep 16 '20 at 08:14
  • I still don't understand why when applying filter to a subsequence (result of a subscript range) of a string returns a string while applying the filter to self subscript (subsequence as well) in a StringProtocol instance method returns a subsequence. Do you mind expanding on that? – Leo Dabus Sep 16 '20 at 18:07
  • 1
    @LeoDabus: Check this https://github.com/apple/swift/pull/10871: *“Per guidance in SE-163 that methods that return new strings shouldn't return substrings, Substring shouldn't just take the default for RangeReplaceableCollection.filter of returning Self”* – Martin R Sep 16 '20 at 18:17
  • Correct me if I am wrong. If I understood it correctly The filter being called when applying to a substring it is not the same of the one called on a subsequence. There is probably a filter method declared in Substring type. – Leo Dabus Sep 16 '20 at 18:24
  • Found it https://github.com/apple/swift/blob/master/stdlib/public/core/Substring.swift `public func filter( _ isIncluded: (Element) throws -> Bool ) rethrows -> String { return try String(self.lazy.filter(isIncluded)) }` – Leo Dabus Sep 16 '20 at 18:27
  • 1
    @LeoDabus: That is what I meant, it was added with the pull request from my above link. – Martin R Sep 16 '20 at 18:28
1

You can use components(separatedBy:) and joined() to get that working,

func removeDuplicate(string: String, char: String) -> String {
    let arr = string.components(separatedBy: char)
    var result = arr[0]
    if arr.count > 1 {
        result += char + arr[1]
    }
    if arr.count > 2 {
        result += arr[2...].joined()
    }
    return result
}
PGDev
  • 23,751
  • 6
  • 34
  • 88