patternjavaMinor
Find the first unique character in a string
Viewed 0 times
uniquethecharacterfirstfindstring
Problem
Implementation for a relatively basic programming challenge:
Find the first unique character in a string
So evaluating the string
My implementation does the following:
Is there a better approach?
`public Character identifyFirstUniqueCharacterInString(final String string) throws NoUniqueCharactersException {
final char[] chars = string.toCharArray();
final boolean[] repeatedCharacterIndices = new boolean[chars.length];
for (int candidateCharacterIndex = 0; candidateCharacterIndex
Find the first unique character in a string
So evaluating the string
ABCA would return B.My implementation does the following:
- Initialize a
booleanarray that is the size of the input string - note that the default initialization value isfalse
- The
booleanarray's values correspond to whether or not the input string character that shares the same index value is unique or not.
- Compare each character in the input string against all the characters to the right of it
- If there is a match, set the values in the boolean array to true for each character index value.
- Iterate through the boolean array and return the first value that is false.
- If iteration completes, throw a
NoUniqueCharactersException
Is there a better approach?
- I briefly considered using some sort of
Ordered Map(if such a thing exists) that maps characters to their counts where the characterkeyvalues are ordered by their index in the string
`public Character identifyFirstUniqueCharacterInString(final String string) throws NoUniqueCharactersException {
final char[] chars = string.toCharArray();
final boolean[] repeatedCharacterIndices = new boolean[chars.length];
for (int candidateCharacterIndex = 0; candidateCharacterIndex
Solution
2021-10-31:
Wells... as pointed out by @RiaD's comment, the original answer below failed to account for values seen. That means a simple string like
I think where we got befuddled was trying to keep a mix of arrays in order to know what's unique and where they are. An alternative, arguably simpler way, is to use a map and then iterating on it. (so two iterations...?)
-
The first iteration collects into a
-
The next iteration on the new map keeps only the valid positions (
(wrong answer provided below, keeping it as a learning lesson...)
One alternative is to let
A second alternative is to use
In this case, if the last index of the character at position
Wells... as pointed out by @RiaD's comment, the original answer below failed to account for values seen. That means a simple string like
aabc will return the second a, as it wrongly thinks it's unique having not appeared again.I think where we got befuddled was trying to keep a mix of arrays in order to know what's unique and where they are. An alternative, arguably simpler way, is to use a map and then iterating on it. (so two iterations...?)
-
The first iteration collects into a
Map, with a merging function that returns a -1 when we encounter repeated values.-
The next iteration on the new map keeps only the valid positions (
>= 0), sorts by the value and returns the minimum one.public static char firstUnique(String input) {
return IntStream.range(0, input.length()).boxed() // first iteration
.collect(Collectors.toMap(input::charAt, i -> i, (a, b) -> -1))
.entrySet().stream() // second iteration
.filter(entry -> entry.getValue() >= 0)
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow(NoUniqueCharactersException::new);
}(wrong answer provided below, keeping it as a learning lesson...)
One alternative is to let
String.indexOf(int, int) do the iteration for you:return IntStream.range(0, input.length())
.filter(i -> input.indexOf(input.charAt(i), i + 1) == -1)
.mapToObj(i -> Character.valueOf(input.charAt(i)))
.findFirst().orElseThrow(NoUniqueCharactersException::new);- Loop with
IntStreamfrom0(inclusive) to the length ofinput(exclusive).
- Filter for indices
iwhereinput.indexOf(input.charAt(i), i + 1)gives-1, i.e. there is no occurrence of the character at indexipast that position.
- For any matches, convert to our desired
Characterwrapper class usingmapToObj().
- In order to return the first character, we
findFirst()from theIntStream,orElseThrow()our customNoUniqueCharactersException.
A second alternative is to use
String.lastIndexOf(int):// instead of this
// .filter(i -> input.indexOf(input.charAt(i), i + 1) == -1)
// use this
.filter(i -> i == input.lastIndexOf(input.charAt(i)))In this case, if the last index of the character at position
i is indeed i, that means we have found our unique character.Code Snippets
public static char firstUnique(String input) {
return IntStream.range(0, input.length()).boxed() // first iteration
.collect(Collectors.toMap(input::charAt, i -> i, (a, b) -> -1))
.entrySet().stream() // second iteration
.filter(entry -> entry.getValue() >= 0)
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow(NoUniqueCharactersException::new);
}return IntStream.range(0, input.length())
.filter(i -> input.indexOf(input.charAt(i), i + 1) == -1)
.mapToObj(i -> Character.valueOf(input.charAt(i)))
.findFirst().orElseThrow(NoUniqueCharactersException::new);// instead of this
// .filter(i -> input.indexOf(input.charAt(i), i + 1) == -1)
// use this
.filter(i -> i == input.lastIndexOf(input.charAt(i)))Context
StackExchange Code Review Q#112272, answer score: 4
Revisions (0)
No revisions yet.