6
\$\begingroup\$

The task is writing a function, which checks if a string contains only unique characters. Means: is each character is included only one time.

Here's the solution, I have developed:

func areAllLettersUnique(_ str: String) -> Bool {
    let arr = str.split(separator: "")
    for (i, chr) in arr.enumerated() {
        if arr[i + 1..<arr.count].contains(chr) {
            return false
        }
    }
    
    return true
}

assert(areAllLettersUnique("No duplicates") == true, "Challenge 1.1 failed")
assert(areAllLettersUnique("abcdefghijklmnopqrstuvwxyz") == true, "Challenge 1.2 failed")
assert(areAllLettersUnique("AaBbCc") == true, "Challenge 1.3 failed")
assert(areAllLettersUnique("Hello, world") == false, "Challenge 1.4 failed")
print("All assertions successful")

It has passed the provided asserts and I think it works correct.

But: Is there a better, perhaps more "swifty" solution?

Can I expect my code to work correctly or are there flaws?

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Not familiar with swift, but generally if you make a Set from the string, and compare its length with the original strings length, you get the answer without (explicit) iteration. \$\endgroup\$
    – morbusg
    May 13, 2023 at 14:31
  • \$\begingroup\$ @morbusg Great idea. Thanks. \$\endgroup\$ May 14, 2023 at 4:51
  • \$\begingroup\$ @morbusg Works perfectly fine. I tried it out. \$\endgroup\$ May 14, 2023 at 4:58

1 Answer 1

6
\$\begingroup\$

This

let arr = str.split(separator: "")

creates an array of one-element substrings of the original string. It is simpler to create an array of Characters with

let arr = Array(str)

It might also be a bit more efficient to compare characters instead of substrings. Also this works not only for strings but for arbitrary Sequences.

if arr[i + 1..<arr.count].contains(chr)

can be simplified slightly using a RangeExpression, in this case ...

if arr[(i + 1)...].contains(chr)

The variable name arr does not indicate what it contains, a better choice might be something like allCharacters.


Your method is not very efficient, the complexity is \$ O(N^2) \$ where \$ N \$ is the length of the string.

One can use a Set to store the distinct characters of the string. A very short and simple implementation would be

func areAllLettersUnique(_ str: String) -> Bool {
    let allCharacters = Set(str)
    return allCharacters.count == str.count
}

but this does not “short-circuit”: The entire string is processed even if a duplicate character has been found.

But one can enumerate the characters and keep track of all characters seen to far:

func areAllLettersUnique(_ str: String) -> Bool {
    var charactersSeenSoFar = Set<Character>()
    for chr in str {
        if charactersSeenSoFar.contains(chr) {
            return false
        }
        charactersSeenSoFar.insert(chr)
    }
    return true
}

The insert operation of a Set returns a value which indicates whether the element was newly inserted or already present, so this can be further simplified to

func areAllLettersUnique(_ str: String) -> Bool {
    var charactersSeenSoFar = Set<Character>()
    for chr in str {
        if !charactersSeenSoFar.insert(chr).inserted {
            return false
        }
    }
    return true
}

Finally note that this method can be made generic with minor changes, so that it can be used with arbitrary sequences of (hashable) elements

func areAllElementsUnique<C>(_ seq: C) -> Bool
where C: Sequence, C.Element: Hashable
{
    var elementsSeenSoFar = Set<C.Element>()
    for elem in seq {
        if !elementsSeenSoFar.insert(elem).inserted {
            return false
        }
    }
    return true
}
\$\endgroup\$
2
  • \$\begingroup\$ Addendum: There is a trivial case where the length of the string is greater than the considered alphabet size \$\endgroup\$
    – ielyamani
    May 15, 2023 at 14:01
  • 2
    \$\begingroup\$ @ielyamani: Sure, that might be useful in programming contests where a restriction on the alphabet it given, e.g. only latin characters. – For a general purpose routine that might not be relevant, a Swift String is a collection of “extended grapheme clusters” and the Unicode Standard contains 149,186 characters \$\endgroup\$
    – Martin R
    May 15, 2023 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.