Determine if a string has all unique characters
Given a character string (which may be empty, or have a length of zero characters):
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
- create a function/procedure/routine to:
- determine if all the characters in the string are unique
- indicate if or which character is duplicated and where
- display each string and its length (as the strings are being examined)
- a zero─length (empty) string shall be considered as unique
- process the strings from left─to─right
- if unique, display a message saying such
- if not unique, then:
- display a message saying such
- display what character is duplicated
- only the 1st non─unique character need be displayed
- display where "both" duplicated characters are in the string
- the above messages can be part of a single message
- display the hexadecimal value of the duplicated character
Use (at least) these five test values (strings):
- a string of length 0 (an empty string)
- a string of length 1 which is a single period (.)
- a string of length 6 which contains: abcABC
- a string of length 7 which contains a blank in the middle: XYZ ZYX
- a string of length 36 which doesn't contain the letter "oh":
- 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ
Show all output here on this page.
- Metrics
- Counting
- Word frequency
- Letter frequency
- Jewels and stones
- I before E except after C
- Bioinformatics/base count
- Count occurrences of a substring
- Count how many vowels and consonants occur in a string
- Remove/replace
- XXXX redacted
- Conjugate a Latin verb
- Remove vowels from a string
- String interpolation (included)
- Strip block comments
- Strip comments from a string
- Strip a set of characters from a string
- Strip whitespace from a string -- top and tail
- Strip control codes and extended characters from a string
- Anagrams/Derangements/shuffling
- Word wheel
- ABC problem
- Sattolo cycle
- Knuth shuffle
- Ordered words
- Superpermutation minimisation
- Textonyms (using a phone text pad)
- Anagrams
- Anagrams/Deranged anagrams
- Permutations/Derangements
- Find/Search/Determine
- ABC words
- Odd words
- Word ladder
- Semordnilap
- Word search
- Wordiff (game)
- String matching
- Tea cup rim text
- Alternade words
- Changeable words
- State name puzzle
- String comparison
- Unique characters
- Unique characters in each string
- Extract file extension
- Levenshtein distance
- Palindrome detection
- Common list elements
- Longest common suffix
- Longest common prefix
- Compare a list of strings
- Longest common substring
- Find common directory path
- Words from neighbour ones
- Change e letters to i in words
- Non-continuous subsequences
- Longest common subsequence
- Longest palindromic substrings
- Longest increasing subsequence
- Words containing "the" substring
- Sum of the digits of n is substring of n
- Determine if a string is numeric
- Determine if a string is collapsible
- Determine if a string is squeezable
- Determine if a string has all unique characters
- Determine if a string has all the same characters
- Longest substrings without repeating characters
- Find words which contains all the vowels
- Find words which contain the most consonants
- Find words which contains more than 3 vowels
- Find words whose first and last three letters are equal
- Find words with alternating vowels and consonants
- Formatting
- Substring
- Rep-string
- Word wrap
- String case
- Align columns
- Literals/String
- Repeat a string
- Brace expansion
- Brace expansion using ranges
- Reverse a string
- Phrase reversals
- Comma quibbling
- Special characters
- String concatenation
- Substring/Top and tail
- Commatizing numbers
- Reverse words in a string
- Suffixation of decimal numbers
- Long literals, with continuations
- Numerical and alphabetical suffixes
- Abbreviations, easy
- Abbreviations, simple
- Abbreviations, automatic
- Song lyrics/poems/Mad Libs/phrases
- Mad Libs
- Magic 8-ball
- 99 bottles of beer
- The Name Game (a song)
- The Old lady swallowed a fly
- The Twelve Days of Christmas
- Tokenize
- Text between
- Tokenize a string
- Word break problem
- Tokenize a string with escaping
- Split a character string based on change of character
- Sequences
11l
F processString(input)
[Char = Int] charMap
V dup = Char("\0")
V index = 0
V pos1 = -1
V pos2 = -1
L(key) input
index++
I key C charMap
dup = key
pos1 = charMap[key]
pos2 = index
L.break
charMap[key] = index
V unique = I dup == Char("\0") {‘yes’} E ‘no’
V diff = I dup == Char("\0") {‘’} E ‘'’dup‘'’
V hexs = I dup == Char("\0") {‘’} E hex(dup.code)
V position = I dup == Char("\0") {‘’} E pos1‘ ’pos2
print(‘#<40 #<6 #<10 #<8 #<3 #<5’.format(input, input.len, unique, diff, hexs, position))
print(‘#<40 #2 #10 #8 #. #.’.format(‘String’, ‘Length’, ‘All Unique’, ‘1st Diff’, ‘Hex’, ‘Positions’))
print(‘#<40 #2 #10 #8 #. #.’.format(‘------------------------’, ‘------’, ‘----------’, ‘--------’, ‘---’, ‘---------’))
L(s) [‘’, ‘.’, ‘abcABC’, ‘XYZ ZYX’, ‘1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ’]
processString(s)
- Output:
String Length All Unique 1st Diff Hex Positions ------------------------ ------ ---------- -------- --- --------- 0 yes . 1 yes abcABC 6 yes XYZ ZYX 7 no 'Z' 5A 3 5 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ 36 no '0' 30 10 25
Action!
PROC PrintBH(BYTE a)
BYTE ARRAY hex=['0 '1 '2 '3 '4 '5 '6 '7 '8 '9 'A 'B 'C 'D 'E 'F]
Put(hex(a RSH 4))
Put(hex(a&$0F))
RETURN
PROC Test(CHAR ARRAY s)
BYTE i,j,n,pos1,pos2
pos1=0 pos2=0
n=s(0)-1
IF n=255 THEN n=0 FI
FOR i=1 TO n
DO
FOR j=i+1 TO s(0)
DO
IF s(j)=s(i) THEN
pos1=i
pos2=j
EXIT
FI
OD
IF pos1#0 THEN
EXIT
FI
OD
PrintF("""%S"" (len=%B) -> ",s,s(0))
IF pos1=0 THEN
PrintE("all characters are unique.")
ELSE
PrintF("""%C"" (hex=$",s(pos1))
PrintBH(s(pos1))
PrintF(") is duplicated at pos. %B and %B.%E",pos1,pos2)
FI
PutE()
RETURN
PROC Main()
Test("")
Test(".")
Test("abcABC")
Test("XYZ ZYX")
Test("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")
RETURN
- Output:
Screenshot from Atari 8-bit computer
"" (len=0) -> all characters are unique. "." (len=1) -> all characters are unique. "abcABC" (len=6) -> all characters are unique. "XYZ ZYX" (len=7) -> "X" (hex=$58) is duplicated at pos. 1 and 7. "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (len=36) -> "0" (hex=$30) is duplicated at pos. 10 and 25.
Ada
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_All_Chars_Unique is
procedure All_Chars_Unique (S : in String) is
begin
Put_Line ("Input = """ & S & """, length =" & S'Length'Image);
for I in S'First .. S'Last - 1 loop
for J in I + 1 .. S'Last loop
if S(I) = S(J) then
Put (" First duplicate at positions" & I'Image &
" and" & J'Image & ", character = '" & S(I) &
"', hex = ");
Put (Character'Pos (S(I)), Width => 0, Base => 16);
New_Line;
return;
end if;
end loop;
end loop;
Put_Line (" All characters are unique.");
end All_Chars_Unique;
begin
All_Chars_Unique ("");
All_Chars_Unique (".");
All_Chars_Unique ("abcABC");
All_Chars_Unique ("XYZ ZYX");
All_Chars_Unique ("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");
end Test_All_Chars_Unique;
- Output:
Input = "", length = 0 All characters are unique. Input = ".", length = 1 All characters are unique. Input = "abcABC", length = 6 All characters are unique. Input = "XYZ ZYX", length = 7 First duplicate at positions 1 and 7, character = 'X', hex = 16#58# Input = "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", length = 36 First duplicate at positions 10 and 25, character = '0', hex = 16#30#
ALGOL 68
BEGIN
# mode to hold the positions of duplicate characters in a string #
MODE DUPLICATE = STRUCT( INT original, first duplicate );
# finds the first non-unique character in s and returns its position #
# and the position of the original character in a DUPLICATE #
# if all characters in s are uniue, returns LWB s - 1, UPB s + 1 #
PROC first duplicate position = ( STRING s )DUPLICATE:
BEGIN
BOOL all unique := TRUE;
INT o pos := LWB s - 1;
INT d pos := UPB s + 1;
FOR i FROM LWB s TO UPB s WHILE all unique DO
FOR j FROM i + 1 TO UPB s WHILE all unique DO
IF NOT ( all unique := s[ i ] /= s[ j ] ) THEN
o pos := i;
d pos := j
FI
OD
OD;
DUPLICATE( o pos, d pos )
END # first duplicate position # ;
# task test cases #
[]STRING tests = ( "", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" );
FOR t pos FROM LWB tests TO UPB tests DO
IF STRING s = tests[ t pos ];
DUPLICATE d = first duplicate position( s );
print( ( "<<<", s, ">>> (length ", whole( ( UPB s + 1 ) - LWB s, 0 ), "): " ) );
original OF d < LWB s
THEN
print( ( " all characters are unique", newline ) )
ELSE
# have at least one duplicate #
print( ( " first duplicate character: """, s[ original OF d ], """"
, " at: ", whole( original OF d, 0 ), " and ", whole( first duplicate OF d, 0 )
, newline
)
)
FI
OD
END
- Output:
<<<>>> (length 0): all characters are unique <<<.>>> (length 1): all characters are unique <<<abcABC>>> (length 6): all characters are unique <<<XYZ ZYX>>> (length 7): first duplicate character: "X" at: 1 and 7 <<<1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ>>> (length 36): first duplicate character: "0" at: 10 and 25
AppleScript
Following AppleScript's convention of one-based indices:
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
on run
script showSource
on |λ|(s)
quoted("'", s) & " (" & length of s & ")"
end |λ|
end script
script showDuplicate
on |λ|(mb)
script go
on |λ|(tpl)
set {c, ixs} to tpl
quoted("'", c) & " at " & intercalate(", ", ixs)
end |λ|
end script
maybe("None", go, mb)
end |λ|
end script
fTable("Indices (1-based) of any duplicated characters:\n", ¬
showSource, showDuplicate, ¬
duplicatedCharIndices, ¬
{"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"})
end run
------------------CHARACTER DUPLICATIONS-------------------
-- duplicatedCharIndices :: String -> Maybe (Char, [Int])
on duplicatedCharIndices(s)
script positionRecord
on |λ|(dct, c, i)
set k to (id of c) as string
script additional
on |λ|(xs)
insertDict(k, xs & i, dct)
end |λ|
end script
maybe(insertDict(k, {i}, dct), additional, lookupDict(k, dct))
end |λ|
end script
script firstDuplication
on |λ|(sofar, idxs)
set {iCode, xs} to idxs
if 1 < length of xs then
script earliest
on |λ|(kxs)
if item 1 of xs < (item 1 of (item 2 of kxs)) then
Just({chr(iCode), xs})
else
sofar
end if
end |λ|
end script
maybe(Just({chr(iCode), xs}), earliest, sofar)
else
sofar
end if
end |λ|
end script
foldl(firstDuplication, Nothing(), ¬
assocs(foldl(positionRecord, {name:""}, chars(s))))
end duplicatedCharIndices
--------------------------GENERIC--------------------------
-- Just :: a -> Maybe a
on Just(x)
-- Constructor for an inhabited Maybe (option type) value.
-- Wrapper containing the result of a computation.
{type:"Maybe", Nothing:false, Just:x}
end Just
-- Nothing :: Maybe a
on Nothing()
-- Constructor for an empty Maybe (option type) value.
-- Empty wrapper returned where a computation is not possible.
{type:"Maybe", Nothing:true}
end Nothing
-- Tuple (,) :: a -> b -> (a, b)
on Tuple(a, b)
-- Constructor for a pair of values, possibly of two different types.
{type:"Tuple", |1|:a, |2|:b, length:2}
end Tuple
-- assocs :: Map k a -> [(k, a)]
on assocs(m)
script go
on |λ|(k)
set mb to lookupDict(k, m)
if true = |Nothing| of mb then
{}
else
{{k, |Just| of mb}}
end if
end |λ|
end script
concatMap(go, keys(m))
end assocs
-- keys :: Dict -> [String]
on keys(rec)
(current application's ¬
NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list
end keys
-- chr :: Int -> Char
on chr(n)
character id n
end chr
-- chars :: String -> [Char]
on chars(s)
characters of s
end chars
-- compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
on compose(f, g)
script
property mf : mReturn(f)
property mg : mReturn(g)
on |λ|(x)
mf's |λ|(mg's |λ|(x))
end |λ|
end script
end compose
-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
set lng to length of xs
set acc to {}
tell mReturn(f)
repeat with i from 1 to lng
set acc to acc & (|λ|(item i of xs, i, xs))
end repeat
end tell
return acc
end concatMap
-- enumFromTo :: Int -> Int -> [Int]
on enumFromTo(m, n)
if m ≤ n then
set lst to {}
repeat with i from m to n
set end of lst to i
end repeat
lst
else
{}
end if
end enumFromTo
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- fst :: (a, b) -> a
on fst(tpl)
if class of tpl is record then
|1| of tpl
else
item 1 of tpl
end if
end fst
-- fTable :: String -> (a -> String) -> (b -> String) -> (a -> b) -> [a] -> String
on fTable(s, xShow, fxShow, f, xs)
set ys to map(xShow, xs)
set w to maximum(map(my |length|, ys))
script arrowed
on |λ|(a, b)
justifyRight(w, space, a) & " -> " & b
end |λ|
end script
s & linefeed & unlines(zipWith(arrowed, ¬
ys, map(compose(fxShow, f), xs)))
end fTable
-- insertDict :: String -> a -> Dict -> Dict
on insertDict(k, v, rec)
tell current application
tell dictionaryWithDictionary_(rec) of its NSMutableDictionary
its setValue:v forKey:(k as string)
it as record
end tell
end tell
end insertDict
-- intercalate :: String -> [String] -> String
on intercalate(delim, xs)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, delim}
set str to xs as text
set my text item delimiters to dlm
str
end intercalate
-- justifyRight :: Int -> Char -> String -> String
on justifyRight(n, cFiller, strText)
if n > length of strText then
text -n thru -1 of ((replicate(n, cFiller) as text) & strText)
else
strText
end if
end justifyRight
-- length :: [a] -> Int
on |length|(xs)
set c to class of xs
if list is c or string is c then
length of xs
else
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
end if
end |length|
-- lookupDict :: a -> Dict -> Maybe b
on lookupDict(k, dct)
-- Just the value of k in the dictionary,
-- or Nothing if k is not found.
set ca to current application
set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k
if missing value ≠ v then
Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list))
else
Nothing()
end if
end lookupDict
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f
-- to each element of xs.
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- maximum :: Ord a => [a] -> a
on maximum(xs)
script
on |λ|(a, b)
if a is missing value or b > a then
b
else
a
end if
end |λ|
end script
foldl(result, missing value, xs)
end maximum
-- maybe :: b -> (a -> b) -> Maybe a -> b
on maybe(v, f, mb)
-- The 'maybe' function takes a default value, a function, and a 'Maybe'
-- value. If the 'Maybe' value is 'Nothing', the function returns the
-- default value. Otherwise, it applies the function to the value inside
-- the 'Just' and returns the result.
if Nothing of mb then
v
else
tell mReturn(f) to |λ|(Just of mb)
end if
end maybe
-- min :: Ord a => a -> a -> a
on min(x, y)
if y < x then
y
else
x
end if
end min
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper.
if script is class of f then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- quoted :: Char -> String -> String
on quoted(c, s)
-- string flanked on both sides
-- by a specified quote character.
c & s & c
end quoted
-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary
-- assembly of a target length
-- replicate :: Int -> a -> [a]
on replicate(n, a)
set out to {}
if 1 > n then return out
set dbl to {a}
repeat while (1 < n)
if 0 < (n mod 2) then set out to out & dbl
set n to (n div 2)
set dbl to (dbl & dbl)
end repeat
return out & dbl
end replicate
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
set c to class of xs
if list is c then
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
else if string is c then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else if script is c then
set ys to {}
repeat with i from 1 to n
set v to |λ|() of xs
if missing value is v then
return ys
else
set end of ys to v
end if
end repeat
return ys
else
missing value
end if
end take
-- unlines :: [String] -> String
on unlines(xs)
-- A single string formed by the intercalation
-- of a list of strings with the newline character.
set {dlm, my text item delimiters} to ¬
{my text item delimiters, linefeed}
set str to xs as text
set my text item delimiters to dlm
str
end unlines
-- zip :: [a] -> [b] -> [(a, b)]
on zip(xs, ys)
zipWith(Tuple, xs, ys)
end zip
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
set lng to min(|length|(xs), |length|(ys))
if 1 > lng then return {}
set xs_ to take(lng, xs) -- Allow for non-finite
set ys_ to take(lng, ys) -- generators like cycle etc
set lst to {}
tell mReturn(f)
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs_, item i of ys_)
end repeat
return lst
end tell
end zipWith
- Output:
Indices (1-based) of any duplicated characters: '' (0) -> None '.' (1) -> None 'abcABC' (6) -> None 'XYZ ZYX' (7) -> 'X' at 1, 7 '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' at 10, 25
Arturo
strings: [
"", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈", "😍😀🙌💃😍🙌", "🐠🐟🐡🦈🐬🐳🐋🐡"
]
loop strings 'str [
chars: split str
prints ["\"" ++ str ++ "\"" ~"(size |size str|):"]
if? chars = unique chars ->
print "has no duplicates."
else [
seen: #[]
done: false
i: 0
while [and? i<size chars
not? done][
ch: chars\[i]
if? not? key? seen ch [
seen\[ch]: i
]
else [
print ~"has duplicate char `|ch|` on |get seen ch| and |i|"
done: true
]
i: i+1
]
]
]
- Output:
"" (size 0): has no duplicates. "." (size 1): has no duplicates. "abcABC" (size 6): has no duplicates. "XYZ ZYX" (size 7): has duplicate char `Z` on 2 and 4 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (size 36): has duplicate char `0` on 9 and 24 "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (size 39): has duplicate char `0` on 0 and 10 "hétérogénéité" (size 13): has duplicate char `é` on 1 and 3 "🎆🎃🎇🎈" (size 4): has no duplicates. "😍😀🙌💃😍🙌" (size 6): has duplicate char `😍` on 0 and 4 "🐠🐟🐡🦈🐬🐳🐋🐡" (size 8): has duplicate char `🐡` on 2 and 7
AutoHotkey
unique_characters(str){
arr := [], res := ""
for i, v in StrSplit(str)
arr[v] := arr[v] ? arr[v] "," i : i
for i, v in Arr
if InStr(v, ",")
res .= v "|" i " @ " v "`tHex = " format("{1:X}", Asc(i)) "`n"
Sort, res, N
res := RegExReplace(res, "`am)^[\d,]+\|")
res := StrSplit(res, "`n").1
return """" str """`tlength = " StrLen(str) "`n" (res ? "Duplicates Found:`n" res : "Unique Characters")
}
Examples:
test := ["",".","abcABC","XYZ ZYX","1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
for i, v in test
MsgBox % unique_characters(v)
return
Outputs:
"" length = 0 Unique Characters --------------------------- "." length = 1 Unique Characters --------------------------- "abcABC" length = 6 Duplicates Found: a @ 1,4 Hex = 61 --------------------------- "XYZ ZYX" length = 7 Duplicates Found: X @ 1,7 Hex = 58 --------------------------- "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" length = 36 Duplicates Found: 0 @ 10,25 Hex = 30 ---------------------------
AWK
# syntax: GAWK -f DETERMINE_IF_A_STRING_HAS_ALL_UNIQUE_CHARACTERS.AWK
BEGIN {
for (i=0; i<=255; i++) { ord_arr[sprintf("%c",i)] = i } # build array[character]=ordinal_value
n = split(",.,abcABC,XYZ ZYX,1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",arr,",")
for (i in arr) {
width = max(width,length(arr[i]))
}
width += 2
fmt = "| %-*s | %-6s | %-10s | %-8s | %-3s | %-9s |\n"
head1 = head2 = sprintf(fmt,width,"string","length","all unique","1st diff","hex","positions")
gsub(/[^|\n]/,"-",head1)
printf(head1 head2 head1) # column headings
for (i=1; i<=n; i++) {
main(arr[i])
}
printf(head1) # column footing
exit(0)
}
function main(str, c,hex,i,leng,msg,position1,position2,tmp_arr) {
msg = "yes"
leng = length(str)
for (i=1; i<=leng; i++) {
c = substr(str,i,1)
if (c in tmp_arr) {
msg = "no"
first_diff = "'" c "'"
hex = sprintf("%2X",ord_arr[c])
position1 = index(str,c)
position2 = i
break
}
tmp_arr[c] = ""
}
printf(fmt,width,"'" str "'",leng,msg,first_diff,hex,position1 " " position2)
}
function max(x,y) { return((x > y) ? x : y) }
- Output:
|----------------------------------------|--------|------------|----------|-----|-----------| | string | length | all unique | 1st diff | hex | positions | |----------------------------------------|--------|------------|----------|-----|-----------| | '' | 0 | yes | | | | | '.' | 1 | yes | | | | | 'abcABC' | 6 | yes | | | | | 'XYZ ZYX' | 7 | no | 'Z' | 5A | 3 5 | | '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' | 36 | no | '0' | 30 | 10 25 | |----------------------------------------|--------|------------|----------|-----|-----------|
BQN
O(n^2) method used for finding indices.
Hex function and loop similar to Determine if a string has all the same characters
Check←=⌜˜
Hex←⊏⟜(∾"0A"+⟜↕¨10‿26)16{⌽𝕗|⌊∘÷⟜𝕗⍟(↕1+·⌊𝕗⋆⁼1⌈⊢)}
{
𝕊 str:
r←Check str
•Out {
∧´1=+´˘r ? "All characters are unique" ;
i←/⊏(1<+´˘r)/r
ch←(⊑i)⊑str
"'"∾ch∾"' (hex: "∾(Hex ch-@)∾", indices: "∾(•Fmt i)∾") duplicated in string '"∾str∾"'"
}
}¨⟨
""
"."
"abcABC"
"XYZ ZYX"
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
⟩
All characters are unique
All characters are unique
All characters are unique
'X' (hex: 58, indices: ⟨ 0 7 ⟩) duplicated in string 'XYZ ZYX'
'0' (hex: 30, indices: ⟨ 9 24 ⟩) duplicated in string '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ'
C
In interactive mode, strings with spaces have to be enclosed in double quotes ("")
#include<stdbool.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
typedef struct positionList{
int position;
struct positionList *next;
}positionList;
typedef struct letterList{
char letter;
int repititions;
positionList* positions;
struct letterList *next;
}letterList;
letterList* letterSet;
bool duplicatesFound = false;
void checkAndUpdateLetterList(char c,int pos){
bool letterOccurs = false;
letterList *letterIterator,*newLetter;
positionList *positionIterator,*newPosition;
if(letterSet==NULL){
letterSet = (letterList*)malloc(sizeof(letterList));
letterSet->letter = c;
letterSet->repititions = 0;
letterSet->positions = (positionList*)malloc(sizeof(positionList));
letterSet->positions->position = pos;
letterSet->positions->next = NULL;
letterSet->next = NULL;
}
else{
letterIterator = letterSet;
while(letterIterator!=NULL){
if(letterIterator->letter==c){
letterOccurs = true;
duplicatesFound = true;
letterIterator->repititions++;
positionIterator = letterIterator->positions;
while(positionIterator->next!=NULL)
positionIterator = positionIterator->next;
newPosition = (positionList*)malloc(sizeof(positionList));
newPosition->position = pos;
newPosition->next = NULL;
positionIterator->next = newPosition;
}
if(letterOccurs==false && letterIterator->next==NULL)
break;
else
letterIterator = letterIterator->next;
}
if(letterOccurs==false){
newLetter = (letterList*)malloc(sizeof(letterList));
newLetter->letter = c;
newLetter->repititions = 0;
newLetter->positions = (positionList*)malloc(sizeof(positionList));
newLetter->positions->position = pos;
newLetter->positions->next = NULL;
newLetter->next = NULL;
letterIterator->next = newLetter;
}
}
}
void printLetterList(){
positionList* positionIterator;
letterList* letterIterator = letterSet;
while(letterIterator!=NULL){
if(letterIterator->repititions>0){
printf("\n'%c' (0x%x) at positions :",letterIterator->letter,letterIterator->letter);
positionIterator = letterIterator->positions;
while(positionIterator!=NULL){
printf("%3d",positionIterator->position + 1);
positionIterator = positionIterator->next;
}
}
letterIterator = letterIterator->next;
}
printf("\n");
}
int main(int argc,char** argv)
{
int i,len;
if(argc>2){
printf("Usage : %s <Test string>\n",argv[0]);
return 0;
}
if(argc==1||strlen(argv[1])==1){
printf("\"%s\" - Length %d - Contains only unique characters.\n",argc==1?"":argv[1],argc==1?0:1);
return 0;
}
len = strlen(argv[1]);
for(i=0;i<len;i++){
checkAndUpdateLetterList(argv[1][i],i);
}
printf("\"%s\" - Length %d - %s",argv[1],len,duplicatesFound==false?"Contains only unique characters.\n":"Contains the following duplicate characters :");
if(duplicatesFound==true)
printLetterList();
return 0;
}
Output, test strings from the task Determine_if_a_string_has_all_the_same_characters are also included :
abhishek_ghosh@Azure:~/doodles$ ./a.out "" - Length 0 - Contains only unique characters. abhishek_ghosh@Azure:~/doodles$ ./a.out . "." - Length 1 - Contains only unique characters. abhishek_ghosh@Azure:~/doodles$ ./a.out abcABC "abcABC" - Length 6 - Contains only unique characters. abhishek_ghosh@Azure:~/doodles$ ./a.out "XYZ YZX" "XYZ YZX" - Length 7 - Contains the following duplicate characters : 'X' (0x58) at positions : 1 7 'Y' (0x59) at positions : 2 5 'Z' (0x5a) at positions : 3 6 abhishek_ghosh@Azure:~/doodles$ ./a.out 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" - Length 36 - Contains the following duplicate characters : '0' (0x30) at positions : 10 25 abhishek_ghosh@Azure:~/doodles$ ./a.out " " " " - Length 3 - Contains the following duplicate characters : ' ' (0x20) at positions : 1 2 3 abhishek_ghosh@Azure:~/doodles$ ./a.out 2 "2" - Length 1 - Contains only unique characters. abhishek_ghosh@Azure:~/doodles$ ./a.out 333 "333" - Length 3 - Contains the following duplicate characters : '3' (0x33) at positions : 1 2 3 abhishek_ghosh@Azure:~/doodles$ ./a.out .55 ".55" - Length 3 - Contains the following duplicate characters : '5' (0x35) at positions : 2 3 abhishek_ghosh@Azure:~/doodles$ ./a.out tttTTT "tttTTT" - Length 6 - Contains the following duplicate characters : 't' (0x74) at positions : 1 2 3 'T' (0x54) at positions : 4 5 6 abhishek_ghosh@Azure:~/doodles$ ./a.out "4444 444k" "4444 444k" - Length 9 - Contains the following duplicate characters : '4' (0x34) at positions : 1 2 3 4 6 7 8
C#
using System;
using System.Linq;
public class Program
{
static void Main
{
string[] input = {"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"};
foreach (string s in input) {
Console.WriteLine($"\"{s}\" (Length {s.Length}) " +
string.Join(", ",
s.Select((c, i) => (c, i))
.GroupBy(t => t.c).Where(g => g.Count() > 1)
.Select(g => $"'{g.Key}' (0X{(int)g.Key:X})[{string.Join(", ", g.Select(t => t.i))}]")
.DefaultIfEmpty("All characters are unique.")
)
);
}
}
}
- Output:
"" (Length 0) All characters are unique. "." (Length 1) All characters are unique. "abcABC" (Length 6) All characters are unique. "XYZ ZYX" (Length 7) 'X'(0X58) [0, 6], 'Y'(0X59) [1, 5], 'Z'(0X5A) [2, 4] "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (Length 36) '0'(0X30) [9, 24]
C++
#include <iostream>
#include <string>
void string_has_repeated_character(const std::string& str) {
size_t len = str.length();
std::cout << "input: \"" << str << "\", length: " << len << '\n';
for (size_t i = 0; i < len; ++i) {
for (size_t j = i + 1; j < len; ++j) {
if (str[i] == str[j]) {
std::cout << "String contains a repeated character.\n";
std::cout << "Character '" << str[i]
<< "' (hex " << std::hex << static_cast<unsigned int>(str[i])
<< ") occurs at positions " << std::dec << i + 1
<< " and " << j + 1 << ".\n\n";
return;
}
}
}
std::cout << "String contains no repeated characters.\n\n";
}
int main() {
string_has_repeated_character("");
string_has_repeated_character(".");
string_has_repeated_character("abcABC");
string_has_repeated_character("XYZ ZYX");
string_has_repeated_character("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");
return 0;
}
- Output:
input: "", length: 0 String contains no repeated characters. input: ".", length: 1 String contains no repeated characters. input: "abcABC", length: 6 String contains no repeated characters. input: "XYZ ZYX", length: 7 String contains a repeated character. Character 'X' (hex 58) occurs at positions 1 and 7. input: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", length: 36 String contains a repeated character. Character '0' (hex 30) occurs at positions 10 and 25.
Clojure
(defn uniq-char-string [s]
(let [len (count s)]
(if (= len (count (set s)))
(println (format "All %d chars unique in: '%s'" len s))
(loop [prev-chars #{}
idx 0
chars (vec s)]
(let [c (first chars)]
(if (contains? prev-chars c)
(println (format "'%s' (len: %d) has '%c' duplicated at idx: %d"
s len c idx))
(recur (conj prev-chars c)
(inc idx)
(rest chars))))))))
- Output:
All 0 chars unique in: '' All 1 chars unique in: '.' All 6 chars unique in: 'abcABC' 'XYZ ZYX' (len: 7) has 'Z' duplicated at idx: 4 '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (len: 36) has '0' duplicated at idx: 24 All 4 chars unique in: 'asdf' 'asdfas' (len: 6) has 'a' duplicated at idx: 4 'foofoo' (len: 6) has 'o' duplicated at idx: 2 'foOfoo' (len: 6) has 'f' duplicated at idx: 3
Common Lisp
;; * Loading the iterate library
(eval-when (:compile-toplevel :load-toplevel)
(ql:quickload '("iterate")))
;; * The package definition
(defpackage :unique-string
(:use :common-lisp :iterate))
(in-package :unique-string)
;; * The test strings
(defparameter test-strings
'("" "." "abcABC" "XYZ ZYX" "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"))
;; * The function
(defun unique-string (string)
"Returns T if STRING has all unique characters."
(iter
(with hash = (make-hash-table :test #'equal))
(with len = (length string))
(with result = T)
(for char in-string string)
(for pos from 0)
(initially (format t "String ~a of length ~D~%" string len))
(if #1=(gethash char hash)
;; The character was seen before
(progn
(format t
" --> Non-unique character ~c #X~X found at position ~D,
before ~D ~%" char (char-code char) pos #1#)
(setf result nil))
;; The character was not seen before, saving its position
(setf #1# pos))
(finally (when result
(format t " --> All characters are unique~%"))
(return result))))
(mapcar #'unique-string test-strings)
- Output:
String of length 0 --> All characters are unique String . of length 1 --> All characters are unique String abcABC of length 6 --> All characters are unique String XYZ ZYX of length 7 --> Non-unique character Z #X5A found at position 4, before 2 --> Non-unique character Y #X59 found at position 5, before 1 --> Non-unique character X #X58 found at position 6, before 0 String 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ of length 36 --> Non-unique character 0 #X30 found at position 24, before 9
D
import std.stdio;
void uniqueCharacters(string str) {
writefln("input: `%s`, length: %d", str, str.length);
foreach (i; 0 .. str.length) {
foreach (j; i + 1 .. str.length) {
if (str[i] == str[j]) {
writeln("String contains a repeated character.");
writefln("Character '%c' (hex %x) occurs at positions %d and %d.", str[i], str[i], i + 1, j + 1);
writeln;
return;
}
}
}
writeln("String contains no repeated characters.");
writeln;
}
void main() {
uniqueCharacters("");
uniqueCharacters(".");
uniqueCharacters("abcABC");
uniqueCharacters("XYZ ZYX");
uniqueCharacters("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");
}
- Output:
input: ``, length: 0 String contains no repeated characters. input: `.`, length: 1 String contains no repeated characters. input: `abcABC`, length: 6 String contains no repeated characters. input: `XYZ ZYX`, length: 7 String contains a repeated character. Character 'X' (hex 58) occurs at positions 1 and 7. input: `1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ`, length: 36 String contains a repeated character. Character '0' (hex 30) occurs at positions 10 and 25.
Delphi
program Determine_if_a_string_has_all_unique_characters;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
procedure string_has_repeated_character(str: string);
var
len, i, j: Integer;
begin
len := length(str);
Writeln('input: \', str, '\, length: ', len);
for i := 1 to len - 1 do
begin
for j := i + 1 to len do
begin
if str[i] = str[j] then
begin
Writeln('String contains a repeated character.');
Writeln('Character "', str[i], '" (hex ', ord(str[i]).ToHexString,
') occurs at positions ', i + 1, ' and ', j + 1, '.'#10);
Exit;
end;
end;
end;
Writeln('String contains no repeated characters.' + sLineBreak);
end;
begin
string_has_repeated_character('');
string_has_repeated_character('.');
string_has_repeated_character('abcABC');
string_has_repeated_character('XYZ ZYX');
string_has_repeated_character('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ');
readln;
end.
- Output:
Delphi strings are start index in one.
input: \\, length: 0 String contains no repeated characters. input: \.\, length: 1 String contains no repeated characters. input: \abcABC\, length: 6 String contains no repeated characters. input: \XYZ ZYX\, length: 7 String contains a repeated character. Character "X" (hex 0058) occurs at positions 2 and 8. input: \1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ\, length: 36 String contains a repeated character. Character "0" (hex 0030) occurs at positions 11 and 26.
Erlang
-module(string_examples).
-export([all_unique/1, all_unique_examples/0]).
all_unique(String) ->
CharPosPairs = lists:zip(String, lists:seq(1, length(String))),
Duplicates = [{Char1, Pos1, Pos2} || {Char1, Pos1} <- CharPosPairs,
{Char2, Pos2} <- CharPosPairs,
Char1 =:= Char2,
Pos2 > Pos1],
case Duplicates of
[] ->
all_unique;
[{Char, P1, P2}|_] ->
{not_all_unique, Char, P1, P2}
end.
all_unique_examples() ->
lists:foreach(fun (Str) ->
io:format("String \"~ts\" (length ~p): ",
[Str, length(Str)]),
case all_unique(Str) of
all_unique ->
io:format("All characters unique.~n");
{not_all_unique, Char, P1, P2} ->
io:format("First duplicate is '~tc' (0x~.16b)"
" at positions ~p and ~p.~n",
[Char, Char, P1, P2])
end
end,
["", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]).
- Output:
$ erl Erlang/OTP 23 [erts-11.1.8] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] Eshell V11.1.8 (abort with ^G) 1> c(string_examples). {ok,string_examples} 2> string_examples:all_unique_examples(). String "" (length 0): All characters unique. String "." (length 1): All characters unique. String "abcABC" (length 6): All characters unique. String "XYZ ZYX" (length 7): First duplicate is 'X' (0x58) at positions 1 and 7. String "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length 36): First duplicate is '0' (0x30) at positions 10 and 25. ok
F#
// Determine if a string has all unique characters. Nigel Galloway: June 9th., 2020
let fN (n:string)=n.ToCharArray()|>Array.mapi(fun n g->(n,g))|>Array.groupBy(fun (_,n)->n)|>Array.filter(fun(_,n)->n.Length>1)
let allUnique n=match fN n with
g when g.Length=0->printfn "All charcters in <<<%s>>> (length %d) are unique" n n.Length
|g->Array.iter(fun(n,g)->printf "%A is repeated at positions" n; Array.iter(fun(n,_)->printf " %d" n)g;printf " ")g
printfn "in <<<%s>>> (length %d)" n n.Length
allUnique ""
allUnique "."
allUnique "abcABC"
allUnique "XYZ ZYX"
allUnique "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
- Output:
All charcters in <<<>>> (length 0) are unique All charcters in <<<.>>> (length 1) are unique All charcters in <<<abcABC>>> (length 6) are unique 'X' is repeated at positions 0 6 'Y' is repeated at positions 1 5 'Z' is repeated at positions 2 4 in <<<XYZ ZYX>>> (length 7) '0' is repeated at positions 9 24 in <<<1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ>>> (length 36)
Factor
USING: formatting fry generalizations io kernel math.parser
sequences sets ;
: repeated ( elt seq -- )
[ dup >hex over ] dip indices first2
" '%c' (0x%s) at indices %d and %d.\n" printf ;
: uniqueness-report ( str -- )
dup dup length "%u — length %d — contains " printf
[ duplicates ] keep over empty?
[ 2drop "all unique characters." print ]
[ "repeated characters:" print '[ _ repeated ] each ] if ;
""
"."
"abcABC"
"XYZ ZYX"
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
[ uniqueness-report nl ] 5 napply
- Output:
"" — length 0 — contains all unique characters. "." — length 1 — contains all unique characters. "abcABC" — length 6 — contains all unique characters. "XYZ ZYX" — length 7 — contains repeated characters: 'Z' (0x5a) at indices 2 and 4. 'Y' (0x59) at indices 1 and 5. 'X' (0x58) at indices 0 and 6. "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" — length 36 — contains repeated characters: '0' (0x30) at indices 9 and 24.
Fortran
program demo_verify
implicit none
call nodup('')
call nodup('.')
call nodup('abcABC')
call nodup('XYZ ZYX')
call nodup('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ')
contains
subroutine nodup(str)
character(len=*),intent(in) :: str
character(len=*),parameter :: g='(*(g0))'
character(len=:),allocatable :: ch
integer :: where
integer :: i
where=0
ch=''
do i=1,len(str)-1
ch=str(i:i)
where=index(str(i+1:),ch)
if(where.ne.0)then
where=where+i
exit
endif
enddo
if(where.eq.0)then
write(*,g)'STR: "',str,'"',new_line('a'),'LEN: ',len(str),'. No duplicate characters found'
else
write(*,g)'STR: "',str,'"'
write(*,'(a,a,t1,a,a)')repeat(' ',where+5),'^',repeat(' ',i+5),'^'
write(*,g)'LEN: ',len(str), &
& '. Duplicate chars. First duplicate at positions ',i,' and ',where, &
& ' where a ','"'//str(where:where)//'"(hex:',hex(str(where:where)),') was found.'
endif
write(*,*)
end subroutine nodup
function hex(ch) result(hexstr)
character(len=1),intent(in) :: ch
character(len=:),allocatable :: hexstr
hexstr=repeat(' ',100)
write(hexstr,'(Z0)')ch
hexstr=trim(hexstr)
end function hex
end program demo_verify
- Output:
STR: "" LEN: 0. No duplicate characters found STR: "." LEN: 1. No duplicate characters found STR: "abcABC" LEN: 6. No duplicate characters found STR: "XYZ ZYX" ^ ^ LEN: 7. Duplicate chars. First duplicate at positions 1 and 7 where a "X"(hex:58) was found. STR: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" ^ ^ LEN: 36. Duplicate chars. First duplicate at positions 10 and 25 where a "0"(hex:30) was found.
FreeBASIC
Sub CaracteresUnicos (cad As String)
Dim As Integer lngt = Len(cad)
Print "Cadena = """; cad; """, longitud = "; lngt
For i As Integer = 1 To lngt
For j As Integer = i + 1 To lngt
If Mid(cad,i,1) = Mid(cad,j,1) Then
Print " Primer duplicado en las posiciones " & i & _
" y " & j & ", caracter = '" & Mid(cad,i,1) & _
"', valor hex = " & Hex(Asc(Mid(cad,i,1)))
Print
Exit Sub
End If
Next j
Next i
Print " Todos los caracteres son unicos." & Chr(10)
End Sub
CaracteresUnicos ("")
CaracteresUnicos (".")
CaracteresUnicos ("abcABC")
CaracteresUnicos ("XYZ ZYX")
CaracteresUnicos ("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")
Sleep
- Output:
Cadena = "", longitud = 0 Todos los caracteres son unicos. Cadena = ".", longitud = 1 Todos los caracteres son unicos. Cadena = "abcABC", longitud = 6 Todos los caracteres son unicos. Cadena = "XYZ ZYX", longitud = 7 Primer duplicado en las posiciones 1 y 7, caracter = 'X', valor hex = 58 Cadena = "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", longitud = 36 Primer duplicado en las posiciones 10 y 25, caracter = '0', valor hex = 30
Go
package main
import "fmt"
func analyze(s string) {
chars := []rune(s)
le := len(chars)
fmt.Printf("Analyzing %q which has a length of %d:\n", s, le)
if le > 1 {
for i := 0; i < le-1; i++ {
for j := i + 1; j < le; j++ {
if chars[j] == chars[i] {
fmt.Println(" Not all characters in the string are unique.")
fmt.Printf(" %q (%#[1]x) is duplicated at positions %d and %d.\n\n", chars[i], i+1, j+1)
return
}
}
}
}
fmt.Println(" All characters in the string are unique.\n")
}
func main() {
strings := []string{
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡",
}
for _, s := range strings {
analyze(s)
}
}
- Output:
Analyzing "" which has a length of 0: All characters in the string are unique. Analyzing "." which has a length of 1: All characters in the string are unique. Analyzing "abcABC" which has a length of 6: All characters in the string are unique. Analyzing "XYZ ZYX" which has a length of 7: Not all characters in the string are unique. 'X' (0x58) is duplicated at positions 1 and 7. Analyzing "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" which has a length of 36: Not all characters in the string are unique. '0' (0x30) is duplicated at positions 10 and 25. Analyzing "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" which has a length of 39: Not all characters in the string are unique. '0' (0x30) is duplicated at positions 1 and 11. Analyzing "hétérogénéité" which has a length of 13: Not all characters in the string are unique. 'é' (0xe9) is duplicated at positions 2 and 4. Analyzing "🎆🎃🎇🎈" which has a length of 4: All characters in the string are unique. Analyzing "😍😀🙌💃😍🙌" which has a length of 6: Not all characters in the string are unique. '😍' (0x1f60d) is duplicated at positions 1 and 5. Analyzing "🐠🐟🐡🦈🐬🐳🐋🐡" which has a length of 8: Not all characters in the string are unique. '🐡' (0x1f421) is duplicated at positions 3 and 8.
Groovy
class StringUniqueCharacters {
static void main(String[] args) {
printf("%-40s %2s %10s %8s %s %s%n", "String", "Length", "All Unique", "1st Diff", "Hex", "Positions")
printf("%-40s %2s %10s %8s %s %s%n", "------------------------", "------", "----------", "--------", "---", "---------")
for (String s : ["", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]) {
processString(s)
}
}
private static void processString(String input) {
Map<Character, Integer> charMap = new HashMap<>()
char dup = 0
int index = 0
int pos1 = -1
int pos2 = -1
for (char key : input.toCharArray()) {
index++
if (charMap.containsKey(key)) {
dup = key
pos1 = charMap.get(key)
pos2 = index
break
}
charMap.put(key, index)
}
String unique = (int) dup == 0 ? "yes" : "no"
String diff = (int) dup == 0 ? "" : "'" + dup + "'"
String hex = (int) dup == 0 ? "" : Integer.toHexString((int) dup).toUpperCase()
String position = (int) dup == 0 ? "" : pos1 + " " + pos2
printf("%-40s %-6d %-10s %-8s %-3s %-5s%n", input, input.length(), unique, diff, hex, position)
}
}
- Output:
String Length All Unique 1st Diff Hex Positions ------------------------ ------ ---------- -------- --- --------- 0 yes . 1 yes abcABC 6 yes XYZ ZYX 7 no 'Z' 5A 3 5 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ 36 no '0' 30 10 25
Haskell
import Data.List (groupBy, intersperse, sort, transpose)
import Data.Char (ord, toUpper)
import Data.Function(on)
import Numeric (showHex)
hexFromChar :: Char -> String
hexFromChar c = map toUpper $ showHex (ord c) ""
string :: String -> String
string xs = ('\"' : xs) <> "\""
char :: Char -> String
char c = ['\'', c, '\'']
size :: String -> String
size = show . length
positions :: (Int, Int) -> String
positions (a, b) = show a <> " " <> show b
forTable :: String -> [String]
forTable xs = string xs : go (allUnique xs)
where
go Nothing = [size xs, "yes", "", "", ""]
go (Just (u, ij)) = [size xs, "no", char u, hexFromChar u, positions ij]
showTable :: Bool -> Char -> Char -> Char -> [[String]] -> String
showTable _ _ _ _ [] = []
showTable header ver hor sep contents =
unlines $
hr :
(if header
then z : hr : zs
else intersperse hr zss) <>
[hr]
where
vss = map (map length) contents
ms = map maximum (transpose vss) :: [Int]
hr = concatMap (\n -> sep : replicate n hor) ms <> [sep]
top = replicate (length hr) hor
bss = map (map (`replicate` ' ') . zipWith (-) ms) vss
zss@(z:zs) =
zipWith
(\us bs -> concat (zipWith (\x y -> (ver : x) <> y) us bs) <> [ver])
contents
bss
table xs =
showTable
True
'|'
'-'
'+'
(["string", "length", "all unique", "1st diff", "hex", "positions"] :
map forTable xs)
allUnique
:: (Ord b, Ord a, Num b, Enum b)
=> [a] -> Maybe (a, (b, b))
allUnique xs = go . groupBy (on (==) fst) . sort . zip xs $ [0 ..]
where
go [] = Nothing
go ([_]:us) = go us
go (((u, i):(_, j):_):_) = Just (u, (i, j))
main :: IO ()
main =
putStrLn $
table ["", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
- Output:
+--------------------------------------+------+----------+--------+---+---------+ |string |length|all unique|1st diff|hex|positions| +--------------------------------------+------+----------+--------+---+---------+ |"" |0 |yes | | | | |"." |1 |yes | | | | |"abcABC" |6 |yes | | | | |"XYZ ZYX" |7 |no |'X' |58 |0 6 | |"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"|36 |no |'0' |30 |9 24 | +--------------------------------------+------+----------+--------+---+---------+
Alternatively, defining a duplicatedCharIndices function in terms of sortOn, groupBy, and filter:
import Data.List (groupBy, intercalate, sortOn)
import Data.Function (on)
import Numeric (showHex)
import Data.Char (ord)
------------- INDICES OF DUPLICATED CHARACTERS -----------
duplicatedCharIndices :: String -> Maybe (Char, [Int])
duplicatedCharIndices s
| null duplicates = Nothing
| otherwise =
Just $
((,) . (snd . head) <*> fmap fst) (head (sortOn (fst . head) duplicates))
where
duplicates =
filter ((1 <) . length) $
groupBy (on (==) snd) $ sortOn snd $ zip [0 ..] s
--------------------------- TEST -------------------------
main :: IO ()
main =
putStrLn $
fTable
"First duplicated character, if any:"
(fmap (<>) show <*> ((" (" <>) . (<> ")") . show . length))
(maybe
"None"
(\(c, ixs) ->
unwords
[ show c
, "(0x" <> showHex (ord c) ") at"
, intercalate ", " (show <$> ixs)
]))
duplicatedCharIndices
["", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
------------------------- DISPLAY ------------------------
fTable :: String -> (a -> String) -> (b -> String) -> (a -> b) -> [a] -> String
fTable s xShow fxShow f xs =
unlines $
s : fmap (((<>) . rjust w ' ' . xShow) <*> ((" -> " <>) . fxShow . f)) xs
where
rjust n c = drop . length <*> (replicate n c <>)
w = maximum (length . xShow <$> xs)
- Output:
First duplicated character, if any: "" (0) -> None "." (1) -> None "abcABC" (6) -> None "XYZ ZYX" (7) -> 'X' (0x58) at 0, 6 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (36) -> '0' (0x30) at 9, 24
Or, as an alternative to grouping and sorting – folding a string down to a Map of indices:
import qualified Safe as S
import qualified Data.Map.Strict as M
import Data.List (intercalate, foldl') --'
import Data.Ord (comparing)
import Numeric (showHex)
import Data.Char (ord)
----------- INDICES OF ANY DUPLICATED CHARACTERS ---------
duplicatedCharIndices :: String -> Maybe (Char, [Int])
duplicatedCharIndices xs =
S.minimumByMay
(comparing (head . snd))
(M.toList
(M.filter
((1 <) . length)
(foldl' --'
(\a (i, c) -> M.insert c (maybe [i] (<> [i]) (M.lookup c a)) a)
M.empty
(zip [0 ..] xs))))
-- OR, fusing filter, toList, and minimumByMay down to a single fold:
duplicatedCharIndices_ :: String -> Maybe (Char, [Int])
duplicatedCharIndices_ xs =
M.foldrWithKey
go
Nothing
(foldl' --'
(\a (i, c) -> M.insert c (maybe [i] (<> [i]) (M.lookup c a)) a)
M.empty
(zip [0 ..] xs))
where
go k [_] mb = mb -- Unique
go k xs Nothing = Just (k, xs) -- Duplicated
go k xs@(x:_) (Just (c, ys@(y:_)))
| x < y = Just (k, xs) -- Earlier duplication
| otherwise = Just (c, ys)
--------------------------- TEST -------------------------
main :: IO ()
main =
putStrLn $
fTable
"First duplicated character, if any:"
((<>) <$> show <*> ((" (" <>) . (<> ")") . show . length))
(maybe
"None"
(\(c, ixs) ->
unwords
[ show c
, "(0x" <> showHex (ord c) ") at"
, intercalate ", " (show <$> ixs)
]))
duplicatedCharIndices_
["", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
------------------------- DISPLAY ------------------------
fTable :: String -> (a -> String) -> (b -> String) -> (a -> b) -> [a] -> String
fTable s xShow fxShow f xs =
unlines $
s : fmap (((<>) . rjust w ' ' . xShow) <*> ((" -> " <>) . fxShow . f)) xs
where
rjust n c = drop . length <*> (replicate n c <>)
w = maximum (length . xShow <$> xs)
- Output:
First duplicated character, if any: "" (0) -> None "." (1) -> None "abcABC" (6) -> None "XYZ ZYX" (7) -> 'X' (0x58) at 0, 6 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (36) -> '0' (0x30) at 9, 24
J
Quotes surround the literals to make the computed one-at-a-time results present well in the combined table.
rc_unique=: monad define
string=. '"' , y , '"'
self_classification=. = y NB. deprecated- consumes space proportional to the squared tally of y (*: # y)
is_unique=. self_classification =&# y
if. is_unique do.
(# y) ; string ; 'unique'
else.
duplicate_masks=. (#~ (1 < +/"1)) self_classification
duplicate_characters=. ~. y #~ +./ duplicate_masks
ASCII_values_of_duplicates=. a. i. duplicate_characters
markers=. duplicate_masks { ' ^'
A=. (# y) ; string , ' ' ,. markers
B=. 'duplicate' , ASCII_values_of_duplicates ('<' , (#~ 31&<)~ , '> ASCII ' , ":@:[)"0 duplicate_characters
A , < B
end.
)
Tests include those of the C example and a pair of MS-DOS line terminations.
(;:'length string analysis') , rc_unique;._2';.;abcABC;XYZ YZX;1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ; ;2;333;.55;tttTTT;4444 444k;',(4$CRLF),';' ┌──────┬──────────────────────────────────────┬─────────────┐ │length│string │analysis │ ├──────┼──────────────────────────────────────┼─────────────┤ │0 │"" │unique │ ├──────┼──────────────────────────────────────┼─────────────┤ │1 │"." │unique │ ├──────┼──────────────────────────────────────┼─────────────┤ │6 │"abcABC" │unique │ ├──────┼──────────────────────────────────────┼─────────────┤ │7 │"XYZ YZX" │duplicate │ │ │ ^ ^ │<X> ASCII 88 │ │ │ ^ ^ │<Y> ASCII 89 │ │ │ ^ ^ │<Z> ASCII 90 │ ├──────┼──────────────────────────────────────┼─────────────┤ │36 │"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"│duplicate │ │ │ ^ ^ │<0> ASCII 48 │ ├──────┼──────────────────────────────────────┼─────────────┤ │3 │" " │duplicate │ │ │ ^^^ │< > ASCII 32 │ ├──────┼──────────────────────────────────────┼─────────────┤ │1 │"2" │unique │ ├──────┼──────────────────────────────────────┼─────────────┤ │3 │"333" │duplicate │ │ │ ^^^ │<3> ASCII 51 │ ├──────┼──────────────────────────────────────┼─────────────┤ │3 │".55" │duplicate │ │ │ ^^ │<5> ASCII 53 │ ├──────┼──────────────────────────────────────┼─────────────┤ │6 │"tttTTT" │duplicate │ │ │ ^^^ │<t> ASCII 116│ │ │ ^^^ │<T> ASCII 84 │ ├──────┼──────────────────────────────────────┼─────────────┤ │9 │"4444 444k" │duplicate │ │ │ ^^^^ ^^^ │<4> ASCII 52 │ ├──────┼──────────────────────────────────────┼─────────────┤ │4 │" " │duplicate │ │ │ ^ ^ │<> ASCII 13 │ │ │ ^ ^ │<> ASCII 10 │ └──────┴──────────────────────────────────────┴─────────────┘
More uniqueness tests with performance comparison
NB. unique_index answers "Do the left and right indexes match?"
unique_index=: (i. -: i:)~
assert 0 1 -: 2 unique_index\0 0 1
NB. unique_set answers "Are lengths of the nub and original equal?"
unique_set=: -:&# ~.
assert 0 1 -: _2 unique_set\'aab'
NB. unique_nubsieve answers "Are the items unique?"
unique_nubsieve=: 0 -.@:e. ~:
assert 0 1 -: _2 unique_nubsieve\'aab'
Note'compared to nubsieve'
the index method takes 131% longer and 15 times additional memory
the set formation method 15% longer and uses 7 times additional memory.
)
Java
import java.util.HashMap;
import java.util.Map;
// Title: Determine if a string has all unique characters
public class StringUniqueCharacters {
public static void main(String[] args) {
System.out.printf("%-40s %2s %10s %8s %s %s%n", "String", "Length", "All Unique", "1st Diff", "Hex", "Positions");
System.out.printf("%-40s %2s %10s %8s %s %s%n", "------------------------", "------", "----------", "--------", "---", "---------");
for ( String s : new String[] {"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"} ) {
processString(s);
}
}
private static void processString(String input) {
Map<Character,Integer> charMap = new HashMap<>();
char dup = 0;
int index = 0;
int pos1 = -1;
int pos2 = -1;
for ( char key : input.toCharArray() ) {
index++;
if ( charMap.containsKey(key) ) {
dup = key;
pos1 = charMap.get(key);
pos2 = index;
break;
}
charMap.put(key, index);
}
String unique = dup == 0 ? "yes" : "no";
String diff = dup == 0 ? "" : "'" + dup + "'";
String hex = dup == 0 ? "" : Integer.toHexString(dup).toUpperCase();
String position = dup == 0 ? "" : pos1 + " " + pos2;
System.out.printf("%-40s %-6d %-10s %-8s %-3s %-5s%n", input, input.length(), unique, diff, hex, position);
}
}
- Output:
String Length All Unique 1st Diff Hex Positions ------------------------ ------ ---------- -------- --- --------- 0 yes . 1 yes abcABC 6 yes XYZ ZYX 7 no 'Z' 5A 3 5 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ 36 no '0' 30 10 25
Using Java 11
import java.util.HashSet;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
public final class DetermineUniqueCharacters {
public static void main(String[] aArgs) {
List<String> words = List.of( "", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" );
for ( String word : words ) {
Set<Integer> seen = new HashSet<Integer>();
OptionalInt first = word.chars().filter( ch -> ! seen.add(ch) ).findFirst();
if ( first.isPresent() ) {
final char ch = (char) first.getAsInt();
final String hex = Integer.toHexString(ch).toUpperCase();
System.out.println("Word: \"" + word + "\" contains a repeated character.");
System.out.println("Character '" + ch + "' (hex " + hex + ") occurs at positions "
+ word.indexOf(ch) + " and " + word.indexOf(ch, word.indexOf(ch) + 1));
} else {
System.out.println("Word: \"" + word + "\" has all unique characters.");
}
System.out.println();
}
}
}
- Output:
Word: "" has all unique characters. Word: "." has all unique characters. Word: "abcABC" has all unique characters. Word: "XYZ ZYX" contains a repeated character. Character 'Z' (hex 5A) occurs at positions 2 and 4 Word: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" contains a repeated character. Character '0' (hex 30) occurs at positions 9 and 24
JavaScript
(() => {
'use strict';
// duplicatedCharIndices :: String -> Maybe (Char, [Int])
const duplicatedCharIndices = s => {
const
duplicates = filter(g => 1 < g.length)(
groupBy(on(eq)(snd))(
sortOn(snd)(
zip(enumFrom(0))(chars(s))
)
)
);
return 0 < duplicates.length ? Just(
fanArrow(compose(snd, fst))(map(fst))(
sortOn(compose(fst, fst))(
duplicates
)[0]
)
) : Nothing();
};
// ------------------------TEST------------------------
const main = () =>
console.log(
fTable('First duplicated character, if any:')(
s => `'${s}' (${s.length})`
)(maybe('None')(tpl => {
const [c, ixs] = Array.from(tpl);
return `'${c}' (0x${showHex(ord(c))}) at ${ixs.join(', ')}`
}))(duplicatedCharIndices)([
"", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
])
);
// -----------------GENERIC FUNCTIONS------------------
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a => b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// chars :: String -> [Char]
const chars = s => s.split('');
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
x => fs.reduceRight((a, f) => f(a), x);
// enumFrom :: Enum a => a -> [a]
function* enumFrom(x) {
let v = x;
while (true) {
yield v;
v = 1 + v;
}
}
// eq (==) :: Eq a => a -> a -> Bool
const eq = a => b => a === b;
// fanArrow (&&&) :: (a -> b) -> (a -> c) -> (a -> (b, c))
const fanArrow = f =>
// Compose a function from a simple value to a tuple of
// the separate outputs of two different functions.
g => x => Tuple(f(x))(g(x));
// filter :: (a -> Bool) -> [a] -> [a]
const filter = f => xs => xs.filter(f);
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// fTable :: String -> (a -> String) -> (b -> String)
// -> (a -> b) -> [a] -> String
const fTable = s => xShow => fxShow => f => xs => {
// Heading -> x display function ->
// fx display function ->
// f -> values -> tabular string
const
ys = xs.map(xShow),
w = Math.max(...ys.map(length));
return s + '\n' + zipWith(
a => b => a.padStart(w, ' ') + ' -> ' + b
)(ys)(
xs.map(x => fxShow(f(x)))
).join('\n');
};
// groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
const groupBy = fEq =>
// Typical usage: groupBy(on(eq)(f), xs)
xs => 0 < xs.length ? (() => {
const
tpl = xs.slice(1).reduce(
(gw, x) => {
const
gps = gw[0],
wkg = gw[1];
return fEq(wkg[0])(x) ? (
Tuple(gps)(wkg.concat([x]))
) : Tuple(gps.concat([wkg]))([x]);
},
Tuple([])([xs[0]])
);
return tpl[0].concat([tpl[1]])
})() : [];
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite length.
// This enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = f => xs =>
(Array.isArray(xs) ? (
xs
) : xs.split('')).map(f);
// maybe :: b -> (a -> b) -> Maybe a -> b
const maybe = v =>
// Default value (v) if m is Nothing, or f(m.Just)
f => m => m.Nothing ? v : f(m.Just);
// on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
const on = f =>
g => a => b => f(g(a))(g(b));
// ord :: Char -> Int
const ord = c => c.codePointAt(0);
// showHex :: Int -> String
const showHex = n =>
n.toString(16);
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
// sortOn :: Ord b => (a -> b) -> [a] -> [a]
const sortOn = f =>
// Equivalent to sortBy(comparing(f)), but with f(x)
// evaluated only once for each x in xs.
// ('Schwartzian' decorate-sort-undecorate).
xs => xs.map(
x => [f(x), x]
).sort(
(a, b) => a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0)
).map(x => x[1]);
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n => xs =>
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// uncurry :: (a -> b -> c) -> ((a, b) -> c)
const uncurry = f =>
(x, y) => f(x)(y)
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs => ys => {
const
lng = Math.min(length(xs), length(ys)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => Tuple(x)(vs[i]));
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
xs => ys => {
const
lng = Math.min(length(xs), length(ys)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => f(x)(vs[i]));
};
// MAIN ---
return main();
})();
- Output:
First duplicated character, if any: '' (0) -> None '.' (1) -> None 'abcABC' (6) -> None 'XYZ ZYX' (7) -> 'X' (0x58) at 0, 6 '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' (0x30) at 9, 24
Or, as an alternative to sorting and grouping – folding a string down to a dictionary of indices:
(() => {
'use strict';
// duplicatedCharIndices :: String -> Maybe (Char, [Int])
const duplicatedCharIndices = s =>
minimumByMay(
comparing(compose(fst, snd))
)(filter(x => 1 < x[1].length)(
Object.entries(
s.split('').reduce(
(a, c, i) => Object.assign(a, {
[c]: (a[c] || []).concat(i)
}), {}
)
)
));
// ------------------------TEST------------------------
const main = () =>
console.log(
fTable('First duplicated character, if any:')(
s => `'${s}' (${s.length })`
)(maybe('None')(tpl => {
const [c, ixs] = Array.from(tpl);
return `'${c}' (0x${showHex(ord(c))}) at ${ixs.join(', ')}`
}))(duplicatedCharIndices)([
"", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
])
);
// -----------------GENERIC FUNCTIONS------------------
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a => b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// chars :: String -> [Char]
const chars = s => s.split('');
// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
x => y => {
const
a = f(x),
b = f(y);
return a < b ? -1 : (a > b ? 1 : 0);
};
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
x => fs.reduceRight((a, f) => f(a), x);
// enumFrom :: Enum a => a -> [a]
function* enumFrom(x) {
let v = x;
while (true) {
yield v;
v = 1 + v;
}
}
// filter :: (a -> Bool) -> [a] -> [a]
const filter = f => xs => xs.filter(f);
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// fTable :: String -> (a -> String) -> (b -> String)
// -> (a -> b) -> [a] -> String
const fTable = s => xShow => fxShow => f => xs => {
// Heading -> x display function ->
// fx display function ->
// f -> values -> tabular string
const
ys = xs.map(xShow),
w = Math.max(...ys.map(length));
return s + '\n' + zipWith(
a => b => a.padStart(w, ' ') + ' -> ' + b
)(ys)(
xs.map(x => fxShow(f(x)))
).join('\n');
};
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite length.
// This enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
// maybe :: b -> (a -> b) -> Maybe a -> b
const maybe = v =>
// Default value (v) if m is Nothing, or f(m.Just)
f => m => m.Nothing ? v : f(m.Just);
// minimumByMay :: (a -> a -> Ordering) -> [a] -> Maybe a
const minimumByMay = f =>
xs => xs.reduce((a, x) =>
a.Nothing ? Just(x) : (
f(x)(a.Just) < 0 ? Just(x) : a
), Nothing());
// ord :: Char -> Int
const ord = c => c.codePointAt(0);
// showHex :: Int -> String
const showHex = n =>
n.toString(16);
// snd :: (a, b) -> b
const snd = tpl =>
tpl[1];
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = n =>
xs => 'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
xs => ys => {
const
lng = Math.min(length(xs), length(ys)),
vs = take(lng)(ys);
return take(lng)(xs)
.map((x, i) => f(x)(vs[i]));
};
// MAIN ---
return main();
})();
- Output:
First duplicated character, if any: '' (0) -> None '.' (1) -> None 'abcABC' (6) -> None 'XYZ ZYX' (7) -> 'X' (0x58) at 0, 6 '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' (0x30) at 9, 24
jq
"First Duplicate" is here understood to be the first character found to be a duplicate when scanning from left to right, so the First Duplicate in XYYX is Y. It would require only a trivial modification of `firstDuplicate` as defined here to implement the alternative interpretation as the first character to be duplicated.
# Emit null if there is no duplicate, else [c, [ix1, ix2]]
def firstDuplicate:
label $out
| foreach explode[] as $i ({ix: -1};
.ix += 1
| .ix as $ix
| .iu = ([$i] | implode)
| .[.iu] += [ $ix] ;
if .[.iu]|length == 2 then [.iu, .[.iu]], break $out else empty end )
// null ;
Some helper functions for accomplishing other aspects of the task:
# hex of a number or a single (unicode) character
def hex:
def stream:
recurse(if . >= 16 then ./16|floor else empty end) | . % 16 ;
if type=="string" then explode[0] else . end
| [stream] | reverse
| map(if . < 10 then 48 + . else . + 87 end) | implode ;
def lpad($len): tostring | " " * ($len - width) + .;
def q: "«\(.)»";
def header:
"\("string"|q|lpad(38)) : |s| : C : hex IO=0";
def data:
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"😍😀🙌💃😍🙌" ;
The main program:
header,
(data
| firstDuplicate as [$k, $v]
| "\(q|lpad(38)) : \(length|lpad(4)) : \($k // " ") : \($k |if . then hex else " " end) \($v // [])" )
- Output:
«string» : |s| : C : hex IO=0
«» : 0 : : []
«.» : 1 : : []
«abcABC» : 6 : : []
«XYZ ZYX» : 7 : Z : 5A [2,4]
«1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ» : 36 : 0 : 30 [9,24]
«😍😀🙌💃😍🙌» : 6 : 😍:1f60d [0,4]
The last line above was adjusted manually as jq has no built-in function for computing the horizontal "printing" width of unicode strings in general.
Julia
arr(s) = [c for c in s]
alldup(a) = filter(x -> length(x) > 1, [findall(x -> x == a[i], a) for i in 1:length(a)])
firstduplicate(s) = (a = arr(s); d = alldup(a); isempty(d) ? nothing : first(d))
function testfunction(strings)
println("String | Length | All Unique | First Duplicate | Positions\n" *
"-------------------------------------------------------------------------------------")
for s in strings
n = firstduplicate(s)
a = arr(s)
println(rpad(s, 38), rpad(length(s), 11), n == nothing ? "yes" :
rpad("no $(a[n[1]])", 26) * rpad(n[1], 4) * "$(n[2])")
end
end
testfunction([
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡",
])
- Output:
String | Length | All Unique | First Duplicate (Hex) | Positions ------------------------------------------------------------------------------------------- 0 yes . 1 yes abcABC 6 yes XYZ ZYX 7 no X (58) 1 7 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ 36 no 0 (30) 10 25 hétérogénéité 13 no é (e9) 2 4 🎆🎃🎇🎈 4 yes 😍😀🙌💃😍🙌 6 no 😍 (1f60d) 1 5 🐠🐟🐡🦈🐬🐳🐋🐡 8 no 🐡 (1f421) 3 8
Kotlin
import java.util.HashMap
fun main() {
System.out.printf("%-40s %2s %10s %8s %s %s%n", "String", "Length", "All Unique", "1st Diff", "Hex", "Positions")
System.out.printf("%-40s %2s %10s %8s %s %s%n", "------------------------", "------", "----------", "--------", "---", "---------")
for (s in arrayOf("", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")) {
processString(s)
}
}
private fun processString(input: String) {
val charMap: MutableMap<Char, Int?> = HashMap()
var dup = 0.toChar()
var index = 0
var pos1 = -1
var pos2 = -1
for (key in input.toCharArray()) {
index++
if (charMap.containsKey(key)) {
dup = key
pos1 = charMap[key]!!
pos2 = index
break
}
charMap[key] = index
}
val unique = if (dup.toInt() == 0) "yes" else "no"
val diff = if (dup.toInt() == 0) "" else "'$dup'"
val hex = if (dup.toInt() == 0) "" else Integer.toHexString(dup.toInt()).toUpperCase()
val position = if (dup.toInt() == 0) "" else "$pos1 $pos2"
System.out.printf("%-40s %-6d %-10s %-8s %-3s %-5s%n", input, input.length, unique, diff, hex, position)
}
- Output:
String Length All Unique 1st Diff Hex Positions ------------------------ ------ ---------- -------- --- --------- 0 yes . 1 yes abcABC 6 yes XYZ ZYX 7 no 'Z' 5A 3 5 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ 36 no '0' 30 10 25
Lua
Using regular expressions. The '-' in the pattern's '.-' is the "lazy" or "reluctant" repetition qualifier; the usual '.*' would caused pattern to match, in the first example below, the substring "cocc" instead of "coc".
local find, format = string.find, string.format
local function printf(fmt, ...) print(format(fmt,...)) end
local pattern = '(.).-%1' -- '(.)' .. '.-' .. '%1'
function report_dup_char(subject)
local pos1, pos2, char = find(subject, pattern)
local prefix = format('"%s" (%d)', subject, #subject)
if pos1 then
local byte = char:byte()
printf("%s: '%s' (0x%02x) duplicates at %d, %d", prefix, char, byte, pos1, pos2)
else
printf("%s: no duplicates", prefix)
end
end
local show = report_dup_char
show('coccyx')
show('')
show('.')
show('abcABC')
show('XYZ ZYX')
show('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ')
- Output:
"coccyx" (6): 'c' (0x63) duplicates at 1, 3 "" (0): no duplicates "." (1): no duplicates "abcABC" (6): no duplicates "XYZ ZYX" (7): 'X' (0x58) duplicates at 1, 7 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (36): '0' (0x30) duplicates at 10, 25
Maple
CheckUnique:=proc(s)
local i, index;
printf("input: \"%s\", length: %a\n", s, StringTools:-Length(s));
for i from 1 to StringTools:-Length(s) do
index := StringTools:-SearchAll(s[i], s);
if (numelems([index]) > 1) then
printf("The given string has duplicated characters.\n");
printf("The first duplicated character is %a (0x%x) which appears at index %a.\n\n",
s[i], convert(s[i], 'bytes')[1], {index});
return;
end if;
end do;
# if no repeated found
printf("The given string has all unique characters.\n\n");
end proc:
# Test
CheckUnique("");
CheckUnique(".");
CheckUnique("abcABC");
CheckUnique("XYZ ZYX");
CheckUnique("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");
- Output:
input: "", length: 0 The given string has all unique characters. input: ".", length: 1 The given string has all unique characters. input: "abcABC", length: 6 The given string has all unique characters. input: "XYZ ZYX", length: 7 The given string has duplicated characters. The first duplicated character is "X" (0x58) which appears at index {1, 7}. input: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", length: 36 The given string has duplicated characters. The first duplicated character is "0" (0x30) which appears at index {10, 25}.
Mathematica / Wolfram Language
ClearAll[UniqueCharacters]
UniqueCharacters[s_String] := Module[{c, len, good = True},
c = Characters[s];
len = Length[c];
Print[s, " with length ", len];
Do[
If[c[[i]] == c[[j]],
Print["Character ", c[[i]], " is repeated at positions ", i,
" and ", j];
good = False
]
,
{i, len - 1},
{j, i + 1, len}
];
If[good,
Print["No repeats"];
True
,
False
]
]
UniqueCharacters[""]
UniqueCharacters["."]
UniqueCharacters["abcABC"]
UniqueCharacters["XYZ ZYX"]
UniqueCharacters["1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
- Output:
with length 0 No repeats True . with length 1 No repeats True abcABC with length 6 No repeats True XYZ ZYX with length 7 Character X is repeated at positions 1 and 7 Character Y is repeated at positions 2 and 6 Character Z is repeated at positions 3 and 5 False 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ with length 36 Character 0 is repeated at positions 10 and 25 False
Nanoquery
def analyze(s)
s = str(s)
println "Examining [" + s + "] which has a length of " + str(len(s)) + ":"
if len(s) < 2
println "\tAll characters in the string are unique."
return
end
seen = list()
for i in range(0, len(s) - 2)
if s[i] in seen
println "\tNot all characters in the string are unique."
println "\t'" + s[i] + "' " + format("(0x%x)", ord(s[i])) +\
" is duplicated at positions " + str(i + 1) + " and " +\
str(s.indexOf(s[i]) + 1)
return
end
seen.append(s[i])
end
println "\tAll characters in the string are unique."
end
tests = {"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"}
for s in tests
analyze(s)
end
- Output:
Examining [] which has a length of 0: All characters in the string are unique. Examining [.] which has a length of 1: All characters in the string are unique. Examining [abcABC] which has a length of 6: All characters in the string are unique. Examining [XYZ ZYX] which has a length of 7: Not all characters in the string are unique. 'Z' (0x5a) is duplicated at positions 5 and 3 Examining [1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ] which has a length of 36: Not all characters in the string are unique. '0' (0x30) is duplicated at positions 25 and 10
Nim
import unicode, strformat
proc checkUniqueChars(s: string) =
echo fmt"Checking string ""{s}"":"
let runes = s.toRunes
for i in 0..<runes.high:
let rune = runes[i]
for j in (i+1)..runes.high:
if runes[j] == rune:
echo "The string contains duplicate characters."
echo fmt"Character {rune} ({int(rune):x}) is present at positions {i+1} and {j+1}."
echo ""
return
echo "All characters in the string are unique."
echo ""
const Strings = ["",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡"]
for s in Strings:
s.checkUniqueChars()
- Output:
Checking string "": All characters in the string are unique. Checking string ".": All characters in the string are unique. Checking string "abcABC": All characters in the string are unique. Checking string "XYZ ZYX": The string contains duplicate characters. Character X (58) is present at positions 1 and 7. Checking string "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ": The string contains duplicate characters. Character 0 (30) is present at positions 10 and 25. Checking string "hétérogénéité": The string contains duplicate characters. Character é (e9) is present at positions 2 and 4. Checking string "🎆🎃🎇🎈": All characters in the string are unique. Checking string "😍😀🙌💃😍🙌": The string contains duplicate characters. Character 😍 (1f60d) is present at positions 1 and 5. Checking string "🐠🐟🐡🦈🐬🐳🐋🐡": The string contains duplicate characters. Character 🐡 (1f421) is present at positions 3 and 8.
OCaml
Using a map to store characters we've met (as keys) and their first position (as indexes).
module CMap = Map.Make(struct
type t = char
let compare = compare
end)
(** Add index as argument to string.fold_left *)
let string_fold_left_i f acc str =
snd (String.fold_left
(fun (index, acc) char -> (index+1, f acc index char))
(0, acc) str)
exception Found of int * int * char
let has_duplicates str =
try let _ = string_fold_left_i
(fun map index char ->
match CMap.find_opt char map with
| None -> CMap.add char index map
| Some i -> raise (Found (i,index,char)))
CMap.empty str
in Ok ()
with Found (i,j,c) -> Error (i,j,c)
let printer str =
Format.printf "%S (len %d) : " str (String.length str);
match has_duplicates str with
| Ok () -> Format.printf "No duplicates.\n"
| Error (i,j,c) -> Format.printf "Duplicate '%c' (%#x) at %d and %d\n" c (int_of_char c) i j
let () =
printer "";
printer ".";
printer "abcABC";
printer "XYZ ZYX";
printer "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"
- Output:
"" (len 0) : No duplicates. "." (len 1) : No duplicates. "abcABC" (len 6) : No duplicates. "XYZ ZYX" (len 7) : Duplicate 'Z' (0x5a) at 2 and 4 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (len 36) : Duplicate '0' (0x30) at 9 and 24
Perl
use strict;
use warnings;
use feature 'say';
use utf8;
binmode(STDOUT, ':utf8');
use List::AllUtils qw(uniq);
use Unicode::UCD 'charinfo';
for my $str (
'',
'.',
'abcABC',
'XYZ ZYX',
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ',
'01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X',
'Δ👍👨👍Δ',
'ΔδΔ̂ΔΛ',
) {
my @S;
push @S, $1 while $str =~ /(\X)/g;
printf qq{\n"$str" (length: %d) has }, scalar @S;
if (@S != uniq @S ) {
say "duplicated characters:";
my %P;
push @{ $P{$S[$_]} }, 1+$_ for 0..$#S;
for my $k (sort keys %P) {
next unless @{$P{$k}} > 1;
printf "'%s' %s (0x%x) in positions: %s\n", $k, charinfo(ord $k)->{'name'}, ord($k), join ', ', @{$P{$k}};
}
} else {
say "no duplicated characters."
}
}
- Output:
"" (length: 0) has no duplicated characters. "." (length: 1) has no duplicated characters. "abcABC" (length: 6) has no duplicated characters. "XYZ ZYX" (length: 7) has duplicated characters: 'X' LATIN CAPITAL LETTER X (0x58) in positions: 1, 7 'Y' LATIN CAPITAL LETTER Y (0x59) in positions: 2, 6 'Z' LATIN CAPITAL LETTER Z (0x5a) in positions: 3, 5 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length: 36) has duplicated characters: '0' DIGIT ZERO (0x30) in positions: 10, 25 "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (length: 39) has duplicated characters: '0' DIGIT ZERO (0x30) in positions: 1, 11, 26, 38 'X' LATIN CAPITAL LETTER X (0x58) in positions: 35, 39 "Δ👍👨👍Δ" (length: 5) has duplicated characters: 'Δ' GREEK CAPITAL LETTER DELTA (0x394) in positions: 1, 5 '👍' THUMBS UP SIGN (0x1f44d) in positions: 2, 4 "ΔδΔ̂ΔΛ" (length: 5) has duplicated characters: 'Δ' GREEK CAPITAL LETTER DELTA (0x394) in positions: 1, 4
Phix
As with Determine_if_a_string_has_all_the_same_characters#Phix, you can use utf8_to_utf32() when needed.
procedure all_uniq(sequence s) string chars = "" sequence posns = {}, multi = {} integer lm = 0 for i=1 to length(s) do integer si = s[i], k = find(si,chars) if k=0 then chars &= si posns &= {{i}} else posns[k] &= i if length(posns[k])=2 then multi &= k lm += 1 end if end if end for string msg = sprintf("\"%s\" (length %d): ",{s,length(s)}), nod = ordinal(lm,true), ess = "s"[1..lm>1], res = iff(lm=0?"all characters are unique" :sprintf("contains %s duplicate%s:",{nod,ess})) printf(1,"%s %s\n",{msg,res}) res = repeat(' ',length(msg)) for i=1 to length(multi) do integer mi = multi[i], ci = chars[mi] printf(1,"%s '%c'(#%02x) at %V\n",{res,ci,ci,posns[mi]}) end for printf(1,"\n") end procedure constant tests = {"",".","abcABC","XYZ ZYX","1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X", " ","2","333","55","tttTTT","tTTTtt","4444 444k"} papply(tests,all_uniq)
- Output:
"" (length 0): all characters are unique "." (length 1): all characters are unique "abcABC" (length 6): all characters are unique "XYZ ZYX" (length 7): contains three duplicates: 'Z'(#5A) at {3,5} 'Y'(#59) at {2,6} 'X'(#58) at {1,7} "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length 36): contains one duplicate: '0'(#30) at {10,25} "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (length 39): contains two duplicates: '0'(#30) at {1,11,26,38} 'X'(#58) at {35,39} " " (length 3): contains one duplicate: ' '(#20) at {1,2,3} "2" (length 1): all characters are unique "333" (length 3): contains one duplicate: '3'(#33) at {1,2,3} "55" (length 2): contains one duplicate: '5'(#35) at {1,2} "tttTTT" (length 6): contains two duplicates: 't'(#74) at {1,2,3} 'T'(#54) at {4,5,6} "tTTTtt" (length 6): contains two duplicates: 'T'(#54) at {2,3,4} 't'(#74) at {1,5,6} "4444 444k" (length 9): contains one duplicate: '4'(#34) at {1,2,3,4,6,7,8}
The results are effectively printed in order of posns[2], but that can easily be changed, for example by sorting multi before that final print loop.
PicoLisp
(de burn (Lst)
(let P 0
(by
'((A)
(set A (inc 'P))
(put A 'P (char A)) )
group
Lst ) ) )
(de first (Lst)
(mini
'((L)
(nand
(cdr L)
(apply min (mapcar val L)) ) )
Lst ) )
(de uniq? (Str)
(let M (first (burn (chop Str)))
(ifn M
(prinl Str " (length " (length Str) "): all characters are unique")
(prin
Str " (length " (length Str) "): first duplicate character "
(car M)
" at positions " )
(println (mapcar val M)) ) ) )
(uniq?)
(uniq? ".")
(uniq? "abcABC")
(uniq? "XYZ ZYX")
(uniq? "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")
- Output:
(length 0): all characters are unique . (length 1): all characters are unique abcABC (length 6): all characters are unique XYZ ZYX (length 7): first duplicate character X at positions (1 7) 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ (length 36): first duplicate character 0 at positions (10 25)
Prolog
report_duplicates(S) :-
duplicates(S, Dups),
format('For value "~w":~n', S),
report(Dups),
nl.
report(Dups) :-
maplist(only_one_position, Dups),
format(' All characters are unique~n').
report(Dups) :-
exclude(only_one_position, Dups, [c(Char,Positions)|_]),
reverse(Positions, PosInOrder),
atomic_list_concat(PosInOrder, ', ', PosAsList),
format(' The character ~w is non unique at ~p~n', [Char, PosAsList]).
only_one_position(c(_,[_])).
duplicates(S, Count) :-
atom_chars(S, Chars),
char_count(Chars, 0, [], Count).
char_count([], _, C, C).
char_count([C|T], Index, Counted, Result) :-
select(c(C,Positions), Counted, MoreCounted),
succ(Index, Index1),
char_count(T, Index1, [c(C,[Index|Positions])|MoreCounted], Result).
char_count([C|T], Index, Counted, Result) :-
\+ member(c(C,_), Counted),
succ(Index, Index1),
char_count(T, Index1, [c(C,[Index])|Counted], Result).
test :- report_duplicates('').
test :- report_duplicates('.').
test :- report_duplicates('abcABC').
test :- report_duplicates('XYZ ZYX').
test :- report_duplicates('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ').
- Output:
?- forall(test, true). For value "": All characters are unique For value ".": All characters are unique For value "abcABC": All characters are unique For value "XYZ ZYX": The character X is non unique at '0, 6' For value "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ": The character 0 is non unique at '9, 24' true. ?-
Python
Functional
Defined in terms of itertools.groupby:
'''Determine if a string has all unique characters'''
from itertools import groupby
# duplicatedCharIndices :: String -> Maybe (Char, [Int])
def duplicatedCharIndices(s):
'''Just the first duplicated character, and
the indices of its occurrence, or
Nothing if there are no duplications.
'''
def go(xs):
if 1 < len(xs):
duplicates = list(filter(lambda kv: 1 < len(kv[1]), [
(k, list(v)) for k, v in groupby(
sorted(xs, key=swap),
key=snd
)
]))
return Just(second(fmap(fst))(
sorted(
duplicates,
key=lambda kv: kv[1][0]
)[0]
)) if duplicates else Nothing()
else:
return Nothing()
return go(list(enumerate(s)))
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Test over various strings.'''
def showSample(s):
return repr(s) + ' (' + str(len(s)) + ')'
def showDuplicate(cix):
c, ix = cix
return repr(c) + (
' (' + hex(ord(c)) + ') at ' + repr(ix)
)
print(
fTable('First duplicated character, if any:')(
showSample
)(maybe('None')(showDuplicate))(duplicatedCharIndices)([
'', '.', 'abcABC', 'XYZ ZYX',
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ'
])
)
# FORMATTING ----------------------------------------------
# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function -> fx display function ->
f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# GENERIC -------------------------------------------------
# Just :: a -> Maybe a
def Just(x):
'''Constructor for an inhabited Maybe (option type) value.
Wrapper containing the result of a computation.
'''
return {'type': 'Maybe', 'Nothing': False, 'Just': x}
# Nothing :: Maybe a
def Nothing():
'''Constructor for an empty Maybe (option type) value.
Empty wrapper returned where a computation is not possible.
'''
return {'type': 'Maybe', 'Nothing': True}
# fmap :: (a -> b) -> [a] -> [b]
def fmap(f):
'''fmap over a list.
f lifted to a function over a list.
'''
return lambda xs: [f(x) for x in xs]
# fst :: (a, b) -> a
def fst(tpl):
'''First member of a pair.'''
return tpl[0]
# head :: [a] -> a
def head(xs):
'''The first element of a non-empty list.'''
return xs[0] if isinstance(xs, list) else next(xs)
# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
'''Either the default value v, if m is Nothing,
or the application of f to x,
where m is Just(x).
'''
return lambda f: lambda m: v if (
None is m or m.get('Nothing')
) else f(m.get('Just'))
# second :: (a -> b) -> ((c, a) -> (c, b))
def second(f):
'''A simple function lifted to a function over a tuple,
with f applied only to the second of two values.
'''
return lambda xy: (xy[0], f(xy[1]))
# snd :: (a, b) -> b
def snd(tpl):
'''Second member of a pair.'''
return tpl[1]
# swap :: (a, b) -> (b, a)
def swap(tpl):
'''The swapped components of a pair.'''
return (tpl[1], tpl[0])
# MAIN ---
if __name__ == '__main__':
main()
- Output:
First duplicated character, if any: '' (0) -> None '.' (1) -> None 'abcABC' (6) -> None 'XYZ ZYX' (7) -> 'X' (0x58) at [0, 6] '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' (0x30) at [9, 24]
Or, as an alternative to sorting and grouping, folding a string down to a dictionary with reduce:
'''Determine if a string has all unique characters'''
from functools import reduce
# duplicatedCharIndices :: String -> Maybe (Char, [Int])
def duplicatedCharIndices(s):
'''Just the first duplicated character, and
the indices of its occurrence, or
Nothing if there are no duplications.
'''
def go(dct, ic):
i, c = ic
return dict(
dct,
**{c: dct[c] + [i] if c in dct else [i]}
)
duplicates = [
(k, v) for (k, v)
in reduce(go, enumerate(s), {}).items()
if 1 < len(v)
]
return Just(
min(duplicates, key=compose(head, snd))
) if duplicates else Nothing()
# And another alternative here would be to fuse the 1 < len(v)
# filtering, and the min() search for the earliest duplicate,
# down to a single `earliestDuplication` fold:
# duplicatedCharIndices_ :: String -> Maybe (Char, [Int])
def duplicatedCharIndices_(s):
'''Just the first duplicated character, and
the indices of its occurrence, or
Nothing if there are no duplications.
'''
def positionRecord(dct, ic):
i, c = ic
return dict(
dct,
**{c: dct[c] + [i] if c in dct else [i]}
)
def earliestDuplication(sofar, charPosns):
c, indices = charPosns
return (
maybe(Just((c, indices)))(
lambda kxs: Just((c, indices)) if (
# Earlier duplication ?
indices[0] < kxs[1][0]
) else sofar
)(sofar)
) if 1 < len(indices) else sofar
return reduce(
earliestDuplication,
reduce(
positionRecord,
enumerate(s),
{}
).items(),
Nothing()
)
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Test over various strings.'''
def showSample(s):
return repr(s) + ' (' + str(len(s)) + ')'
def showDuplicate(cix):
c, ix = cix
return repr(c) + (
' (' + hex(ord(c)) + ') at ' + repr(ix)
)
print(
fTable('First duplicated character, if any:')(
showSample
)(maybe('None')(showDuplicate))(duplicatedCharIndices_)([
'', '.', 'abcABC', 'XYZ ZYX',
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ'
])
)
# FORMATTING ----------------------------------------------
# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function -> fx display function ->
f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# GENERIC -------------------------------------------------
# Just :: a -> Maybe a
def Just(x):
'''Constructor for an inhabited Maybe (option type) value.
Wrapper containing the result of a computation.
'''
return {'type': 'Maybe', 'Nothing': False, 'Just': x}
# Nothing :: Maybe a
def Nothing():
'''Constructor for an empty Maybe (option type) value.
Empty wrapper returned where a computation is not possible.
'''
return {'type': 'Maybe', 'Nothing': True}
# compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
'''Composition, from right to left,
of a series of functions.
'''
return lambda x: reduce(
lambda a, f: f(a),
fs[::-1], x
)
# head :: [a] -> a
def head(xs):
'''The first element of a non-empty list.'''
return xs[0] if isinstance(xs, list) else next(xs)
# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
'''Either the default value v, if m is Nothing,
or the application of f to x,
where m is Just(x).
'''
return lambda f: lambda m: v if (
None is m or m.get('Nothing')
) else f(m.get('Just'))
# snd :: (a, b) -> b
def snd(tpl):
'''Second member of a pair.'''
return tpl[1]
# MAIN ---
if __name__ == '__main__':
main()
- Output:
First duplicated character, if any: '' (0) -> None '.' (1) -> None 'abcABC' (6) -> None 'XYZ ZYX' (7) -> 'X' (0x58) at [0, 6] '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (36) -> '0' (0x30) at [9, 24]
Using regular expression
The second part of the pattern uses the '*?' match qualifier, which makes the match "lazy" or "reluctant". '.*' instead of '.*?' would have matched the substring "cocc" instead of "coc" in the first example below. Tested with Python 3.7.
import re
pattern = '(.)' + '.*?' + r'\1'
def find_dup_char(subject):
match = re.search(pattern, subject)
if match:
return match.groups(0)[0], match.start(0), match.end(0)
def report_dup_char(subject):
dup = find_dup_char(subject)
prefix = f'"{subject}" ({len(subject)})'
if dup:
ch, pos1, pos2 = dup
print(f"{prefix}: '{ch}' (0x{ord(ch):02x}) duplicates at {pos1}, {pos2-1}")
else:
print(f"{prefix}: no duplicate characters")
show = report_dup_char
show('coccyx')
show('')
show('.')
show('abcABC')
show('XYZ ZYX')
show('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ')
- Output:
"coccyx" (6): 'c' (0x63) duplicates at 0, 2 "" (0): no duplicate characters "." (1): no duplicate characters "abcABC" (6): no duplicate characters "XYZ ZYX" (7): 'X' (0x58) duplicates at 0, 6 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (36): '0' (0x30) duplicates at 9, 24
R
Most of this is adapted from Determine if a string has all the same characters#R.
isAllUnique <- function(string)
{
strLength <- nchar(string)
if(length(strLength) > 1)
{
#R has a distinction between the length of a string and that of a character vector. It is a common source
#of problems when coming from another language. We will try to avoid the topic here.
#For our purposes, let us only say that there is a good reason why we have made
#isAllUnique(c("foo", "bar") immediately throw an error.
stop("This task is intended for character vectors with lengths of at most 1.")
}
else if(length(strLength) == 0)
{
cat("Examining a character vector of length 0.",
"It is therefore made entirely of unique characters.\n")
TRUE
}
else if(strLength == 0)
{
cat("Examining a character vector of length 1, containing an empty string.",
"It is therefore made entirely of unique characters.\n")
TRUE
}
else if(strLength == 1)
{
cat("Examining the string", paste0(sQuote(string), ","),
"which is of length", paste0(strLength, "."),
"It is therefore made entirely of unique characters.\n")
TRUE
}
else
{
cat("Examining the string", paste0(sQuote(string), ","),
"which is of length", paste0(strLength, ":"), "\n")
#strsplit outputs a list. Its first element is the vector of characters that we desire.
characters <- strsplit(string, "")[[1]]
#Our use of match is using R's vector recycling rules. Element i is being checked
#against every other.
indexesOfDuplicates <- sapply(seq_len(strLength), function(i) match(TRUE, characters[i] == characters[-i], nomatch = -1)) + 1
firstDuplicateElementIndex <- indexesOfDuplicates[indexesOfDuplicates != 0][1]
if(is.na(firstDuplicateElementIndex))
{
cat("It has no duplicates. It is therefore made entirely of unique characters.\n")
TRUE
}
else
{
cat("It has duplicates. ")
firstDuplicatedCharacter <- characters[firstDuplicateElementIndex]
cat(sQuote(firstDuplicatedCharacter), "is the first duplicated character. It has hex value",
sprintf("0x%X", as.integer(charToRaw(firstDuplicatedCharacter))),
"and is at index", paste0(firstDuplicateElementIndex, "."),
"\nThis is a duplicate of the character at index",
paste0(match(firstDuplicateElementIndex, indexesOfDuplicates), "."), "\n")
FALSE
}
}
}
#Tests:
cat("Test: A string of length 0 (an empty string):\n")
cat("Test 1 of 2: An empty character vector:\n")
print(isAllUnique(character(0)))
cat("Test 2 of 2: A character vector containing the empty string:\n")
print(isAllUnique(""))
cat("Test: A string of length 1 which contains .:\n")
print(isAllUnique("."))
cat("Test: A string of length 6 which contains abcABC:\n")
print(isAllUnique("abcABC"))
cat("Test: A string of length 7 which contains XYZ ZYX:\n")
print(isAllUnique("XYZ ZYX"))
cat("Test: A string of length 36 doesn't contain the letter 'oh':\n")
print(isAllUnique("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"))
- Output:
Test: A string of length 0 (an empty string): Test 1 of 2: An empty character vector: Examining a character vector of length 0. It is therefore made entirely of unique characters. [1] TRUE Test 2 of 2: A character vector containing the empty string: Examining a character vector of length 1, containing an empty string. It is therefore made entirely of unique characters. [1] TRUE Test: A string of length 1 which contains .: Examining the string ‘.’, which is of length 1. It is therefore made entirely of unique characters. [1] TRUE Test: A string of length 6 which contains abcABC: Examining the string ‘abcABC’, which is of length 6: It has no duplicates. It is therefore made entirely of unique characters. [1] TRUE Test: A string of length 7 which contains XYZ ZYX: Examining the string ‘XYZ ZYX’, which is of length 7: It has duplicates. ‘X’ is the first duplicated character. It has hex value 0x58 and is at index 7. This is a duplicate of the character at index 1. [1] FALSE Test: A string of length 36 doesn't contain the letter 'oh': Examining the string ‘1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ’, which is of length 36: It has duplicates. ‘0’ is the first duplicated character. It has hex value 0x30 and is at index 25. This is a duplicate of the character at index 10. [1] FALSE
Racket
#lang racket
(define (first-non-unique-element.index seq)
(let/ec ret
(for/fold ((es (hash))) ((e seq) (i (in-naturals)))
(if (hash-has-key? es e) (ret (list e (hash-ref es e) i)) (hash-set es e i)))
#f))
(define (report-if-a-string-has-all-unique-characters str)
(printf "~s (length ~a): ~a~%" str (string-length str)
(match (first-non-unique-element.index str)
[#f "contains all unique characters"]
[(list e i i′) (format "has character '~a' (0x~a) at index ~a (first seen at ~a)"
e (number->string (char->integer e) 16) i′ i)])))
(module+ main
(for-each report-if-a-string-has-all-unique-characters
(list "" "." "abcABC" "XYZ ZYX"
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")))
- Output:
"" (length 0): contains all unique characters "." (length 1): contains all unique characters "abcABC" (length 6): contains all unique characters "XYZ ZYX" (length 7): has character 'Z' (0x5a) at index 4 (first seen at 2) "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length 36): has character '0' (0x30) at index 24 (first seen at 9)
Raku
(formerly Perl 6)
Raku works with unicode natively and handles combining characters and multi-byte emoji correctly. In the last string, notice the the length is correctly shown as 11 characters and that the delta with a combining circumflex in position 6 is not the same as the deltas without in positions 5 & 9.
-> $str {
my $i = 0;
print "\n{$str.raku} (length: {$str.chars}), has ";
my %m;
%m{$_}.push: ++$i for $str.comb;
if any(%m.values) > 1 {
say "duplicated characters:";
say "'{.key}' ({.key.uninames}; hex ordinal: {(.key.ords).fmt: "0x%X"})" ~
" in positions: {.value.join: ', '}" for %m.grep( *.value > 1 ).sort( *.value[0] );
} else {
say "no duplicated characters."
}
} for
'',
'.',
'abcABC',
'XYZ ZYX',
'1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ',
'01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X',
'🦋🙂👨👩👧👦🙄ΔΔ̂ 🦋Δ👍👨👩👧👦'
- Output:
"" (length: 0), has no duplicated characters. "." (length: 1), has no duplicated characters. "abcABC" (length: 6), has no duplicated characters. "XYZ ZYX" (length: 7), has duplicated characters: 'X' (LATIN CAPITAL LETTER X; hex ordinal: 0x58) in positions: 1, 7 'Y' (LATIN CAPITAL LETTER Y; hex ordinal: 0x59) in positions: 2, 6 'Z' (LATIN CAPITAL LETTER Z; hex ordinal: 0x5A) in positions: 3, 5 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length: 36), has duplicated characters: '0' (DIGIT ZERO; hex ordinal: 0x30) in positions: 10, 25 "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (length: 39), has duplicated characters: '0' (DIGIT ZERO; hex ordinal: 0x30) in positions: 1, 11, 26, 38 'X' (LATIN CAPITAL LETTER X; hex ordinal: 0x58) in positions: 35, 39 "🦋🙂👨👩👧👦🙄ΔΔ̂ 🦋Δ👍👨👩👧👦" (length: 11), has duplicated characters: '🦋' (BUTTERFLY; hex ordinal: 0x1F98B) in positions: 1, 8 '👨👩👧👦' (MAN ZERO WIDTH JOINER WOMAN ZERO WIDTH JOINER GIRL ZERO WIDTH JOINER BOY; hex ordinal: 0x1F468 0x200D 0x1F469 0x200D 0x1F467 0x200D 0x1F466) in positions: 3, 11 'Δ' (GREEK CAPITAL LETTER DELTA; hex ordinal: 0x394) in positions: 5, 9
REXX
/*REXX pgm determines if a string is comprised of all unique characters (no duplicates).*/
@.= /*assign a default for the @. array. */
parse arg @.1 /*obtain optional argument from the CL.*/
if @.1='' then do; @.1= /*Not specified? Then assume defaults.*/
@.2= .
@.3= 'abcABC'
@.4= 'XYZ ZYX'
@.5= '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ'
end
do j=1; if j\==1 & @.j=='' then leave /*String is null & not j=1? We're done*/
say copies('─', 79) /*display a separator line (a fence). */
say 'Testing for the string (length' length(@.j)"): " @.j
say
dup= isUnique(@.j)
say 'The characters in the string' word("are aren't", 1 + (dup>0) ) 'all unique.'
if dup==0 then iterate
?= substr(@.j, dup, 1)
say 'The character ' ? " ('"c2x(?)"'x) at position " dup ,
' is repeated at position ' pos(?, @.j, dup+1)
end /*j*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
isUnique: procedure; parse arg x /*obtain the character string.*/
do k=1 to length(x) - 1 /*examine all but the last. */
p= pos( substr(x, k, 1), x, k + 1) /*see if the Kth char is a dup*/
if p\==0 then return k /*Find a dup? Return location.*/
end /*k*/
return 0 /*indicate all chars unique. */
- output when using the internal defaults
─────────────────────────────────────────────────────────────────────────────── Testing for the string (length 0): The characters in the string are all unique. ─────────────────────────────────────────────────────────────────────────────── Testing for the string (length 1): . The characters in the string are all unique. ─────────────────────────────────────────────────────────────────────────────── Testing for the string (length 6): abcABC The characters in the string are all unique. ─────────────────────────────────────────────────────────────────────────────── Testing for the string (length 7): XYZ ZYX The characters in the string aren't all unique. The character X ('58'x) at position 1 is repeated at position 7 ─────────────────────────────────────────────────────────────────────────────── Testing for the string (length 36): 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ The characters in the string aren't all unique. The character 0 ('30'x) at position 10 is repeated at position 25
Ring
inputStr = ["",".","abcABC","XYZ ZYX","1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"]
for Str in inputStr
for x = 1 to len(Str)
for y = x + 1 to len(Str)
if Str[x] = Str[y]
char = Str[x]
? "Input = " + "'" + Str + "'" + ", length = " + len(Str)
? " First duplicate at positions " + x + " and " + y + ", character = " + "'" + char + "'"
loop 3
ok
next
next
? "Input = " + "'" + Str + "'" + ", length = " + len(Str)
? " All characters are unique."
next
- Output:
Input = '', length = 0 All characters are unique. Input = '.', length = 1 All characters are unique. Input = 'abcABC', length = 6 All characters are unique. Input = 'XYZ ZYX', length = 7 First duplicate at positions 1 and 7, character = 'X' Input = '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ', length = 36 First duplicate at positions 10 and 25, character = '0'
RPL
RPL code | Comment |
---|---|
≪ → string ≪ "All chars unique" "" 1 string SIZE FOR j string j DUP SUB IF DUP2 POS THEN DUP " duplicated at " + string ROT POS →STR + " and " + j →STR + ROT DROP SWAP string SIZE 'j' STO ELSE + END NEXT DROP ≫ ≫ 'UNICH?' STO |
UNICH? ( "string" -- "report" ) initialize stack scan string extract jth character if already seen generate report . . exit loop else add the char to already seen list clean stack . |
- Input:
≪ { "" "." "abcABC" "XYZ ZYX" "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" } → cases ≪ 1 cases SIZE FOR n cases n GET UNICH? NEXT ≫ ≫ ´TASK’ STO
- Output:
5: "All chars unique" 4: "All chars unique" 3: "All chars unique" 2: "Z duplicated at 3 and 5" 1: "0 duplicated at 10 and 25"
Ruby
strings = ["",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡",]
strings.each do |str|
seen = {}
print "#{str.inspect} (size #{str.size}) "
res = "has no duplicates." #may change
str.chars.each_with_index do |c,i|
if seen[c].nil?
seen[c] = i
else
res = "has duplicate char #{c} (#{'%#x' % c.ord}) on #{seen[c]} and #{i}."
break
end
end
puts res
end
- Output:
"" (size 0) has no duplicates. "." (size 1) has no duplicates. "abcABC" (size 6) has no duplicates. "XYZ ZYX" (size 7) has duplicate char Z (0x5a) on 2 and 4. "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (size 36) has duplicate char 0 (0x30) on 9 and 24. "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (size 39) has duplicate char 0 (0x30) on 0 and 10. "hétérogénéité" (size 13) has duplicate char é (0xe9) on 1 and 3. "🎆🎃🎇🎈" (size 4) has no duplicates. "😍😀🙌💃😍🙌" (size 6) has duplicate char 😍 (0x1f60d) on 0 and 4. "🐠🐟🐡🦈🐬🐳🐋🐡" (size 8) has duplicate char 🐡 (0x1f421) on 2 and 7.
Rust
fn unique(s: &str) -> Option<(usize, usize, char)> {
s.chars().enumerate().find_map(|(i, c)| {
s.chars()
.enumerate()
.skip(i + 1)
.find(|(_, other)| c == *other)
.map(|(j, _)| (i, j, c))
})
}
fn main() {
let strings = [
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡",
];
for string in &strings {
print!("\"{}\" (length {})", string, string.chars().count());
match unique(string) {
None => println!(" is unique"),
Some((i, j, c)) => println!(
" is not unique\n\tfirst duplicate: \"{}\" (U+{:0>4X}) at indices {} and {}",
c, c as usize, i, j
),
}
}
}
- Output:
"" (length 0) is unique "." (length 1) is unique "abcABC" (length 6) is unique "XYZ ZYX" (length 7) is not unique first duplicate: "X" (U+0058) at indices 0 and 6 "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length 36) is not unique first duplicate: "0" (U+0030) at indices 9 and 24 "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" (length 39) is not unique first duplicate: "0" (U+0030) at indices 0 and 10 "hétérogénéité" (length 13) is not unique first duplicate: "é" (U+00E9) at indices 1 and 3 "🎆🎃🎇🎈" (length 4) is unique "😍😀🙌💃😍🙌" (length 6) is not unique first duplicate: "😍" (U+1F60D) at indices 0 and 4 "🐠🐟🐡🦈🐬🐳🐋🐡" (length 8) is not unique first duplicate: "🐡" (U+1F421) at indices 2 and 7
Sidef
func index_duplicates(str) {
gather {
for k,v in (str.chars.kv) {
var i = str.index(v, k+1)
take([k, i]) if (i != -1)
}
}
}
var strings = [
"", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité", "🎆🎃🎇🎈", "😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡"
]
strings.each {|str|
print "\n'#{str}' (size: #{str.len}) "
var dups = index_duplicates(str)
say "has duplicated characters:" if dups
for i,j in (dups) {
say "#{str[i]} (#{'%#x' % str[i].ord}) in positions: #{i}, #{j}"
}
say "has no duplicates." if !dups
}
- Output:
'' (size: 0) has no duplicates. '.' (size: 1) has no duplicates. 'abcABC' (size: 6) has no duplicates. 'XYZ ZYX' (size: 7) has duplicated characters: X (0x58) in positions: 0, 6 Y (0x59) in positions: 1, 5 Z (0x5a) in positions: 2, 4 '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (size: 36) has duplicated characters: 0 (0x30) in positions: 9, 24 '01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X' (size: 39) has duplicated characters: 0 (0x30) in positions: 0, 10 0 (0x30) in positions: 10, 25 0 (0x30) in positions: 25, 37 X (0x58) in positions: 34, 38 'hétérogénéité' (size: 13) has duplicated characters: é (0xe9) in positions: 1, 3 t (0x74) in positions: 2, 11 é (0xe9) in positions: 3, 7 é (0xe9) in positions: 7, 9 é (0xe9) in positions: 9, 12 '🎆🎃🎇🎈' (size: 4) has no duplicates. '😍😀🙌💃😍🙌' (size: 6) has duplicated characters: 😍 (0x1f60d) in positions: 0, 4 🙌 (0x1f64c) in positions: 2, 5 '🐠🐟🐡🦈🐬🐳🐋🐡' (size: 8) has duplicated characters: 🐡 (0x1f421) in positions: 2, 7
Tcl
package require Tcl 8.6 ; # For binary encode
array set yesno {1 Yes 2 No}
set test {
{}
{.}
{abcABC}
{XYZ ZYX}
{1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ}
{hétérogénéité}
}
# Loop through test strings
foreach str $test {
set chars [dict create] ; # init dictionary
set num_chars 1 ; # In case of empty string
# Loop through characters in string
for {set i 0} {$i < [string length $str]} {incr i} {
set c [string index $str $i] ; # get char at index
dict lappend chars $c $i ; # add index to a running list for key=char
set indexes [dict get $chars $c] ; # get the whole running list
set num_chars [llength $indexes] ; # count the # of indexes
if {$num_chars > 1} {
break ; # Found a duplicate, break out of the loop
}
}
# Handle Output
puts [format "Tested: %38s (len: %2d). All unique? %3s. " \
"'$str'" [string length $str] $yesno($num_chars)]
if {$num_chars > 1} {
puts [format " --> Character '%s' (hex: 0x%s) reappears at indexes: %s." \
$c [binary encode hex $c] $indexes]
}
}
- Output:
Tested: '' (len: 0). All unique? Yes. Tested: '.' (len: 1). All unique? Yes. Tested: 'abcABC' (len: 6). All unique? Yes. Tested: 'XYZ ZYX' (len: 7). All unique? No. --> Character 'Z' (hex: 0x5a) reappears at indexes: 2 4. Tested: '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (len: 36). All unique? No. --> Character '0' (hex: 0x30) reappears at indexes: 9 24. Tested: 'hétérogénéité' (len: 13). All unique? No. --> Character 'é' (hex: 0xe9) reappears at indexes: 1 3.
Visual Basic .NET
Module Module1
Sub Main()
Dim input() = {"", ".", "abcABC", "XYZ ZYX", "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"}
For Each s In input
Console.WriteLine($"'{s}' (Length {s.Length}) " + String.Join(", ", s.Select(Function(c, i) (c, i)).GroupBy(Function(t) t.c).Where(Function(g) g.Count() > 1).Select(Function(g) $"'{g.Key}' (0X{AscW(g.Key):X})[{String.Join(", ", g.Select(Function(t) t.i))}]").DefaultIfEmpty("All characters are unique.")))
Next
End Sub
End Module
- Output:
'' (Length 0) All characters are unique. '.' (Length 1) All characters are unique. 'abcABC' (Length 6) All characters are unique. 'XYZ ZYX' (Length 7) 'X' (0X58)[0, 6], 'Y' (0X59)[1, 5], 'Z' (0X5A)[2, 4] '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' (Length 36) '0' (0X30)[9, 24]
V (Vlang)
fn analyze(s string) {
chars := s.runes()
le := chars.len
println("Analyzing $s which has a length of $le:")
if le > 1 {
for i := 0; i < le-1; i++ {
for j := i + 1; j < le; j++ {
if chars[j] == chars[i] {
println(" Not all characters in the string are unique.")
println(" '${chars[i]}'' (0x${chars[i]:x}) is duplicated at positions ${i+1} and ${j+1}.\n")
return
}
}
}
}
println(" All characters in the string are unique.\n")
}
fn main() {
strings := [
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡",
]
for s in strings {
analyze(s)
}
}
- Output:
Analyzing which has a length of 0: All characters in the string are unique. Analyzing . which has a length of 1: All characters in the string are unique. Analyzing abcABC which has a length of 6: All characters in the string are unique. Analyzing XYZ ZYX which has a length of 7: Not all characters in the string are unique. 'X'' (0x58) is duplicated at positions 1 and 7. Analyzing 1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ which has a length of 36: Not all characters in the string are unique. '0'' (0x30) is duplicated at positions 10 and 25. Analyzing 01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X which has a length of 39: Not all characters in the string are unique. '0'' (0x30) is duplicated at positions 1 and 11. Analyzing hétérogénéité which has a length of 13: Not all characters in the string are unique. 'é'' (0xe9) is duplicated at positions 2 and 4. Analyzing 🎆🎃🎇🎈 which has a length of 4: All characters in the string are unique. Analyzing 😍😀🙌💃😍🙌 which has a length of 6: Not all characters in the string are unique. '😍'' (0x1f60d) is duplicated at positions 1 and 5. Analyzing 🐠🐟🐡🦈🐬🐳🐋🐡 which has a length of 8: Not all characters in the string are unique. '🐡'' (0x1f421) is duplicated at positions 3 and 8.
Wren
import "/fmt" for Conv, Fmt
var analyze = Fn.new { |s|
var chars = s.codePoints.toList
var le = chars.count
System.print("Analyzing %(Fmt.q(s)) which has a length of %(le):")
if (le > 1) {
for (i in 0...le-1) {
for (j in i+1...le) {
if (chars[j] == chars[i]) {
System.print(" Not all characters in the string are unique.")
var c = String.fromCodePoint(chars[i])
var hex = "0x" + Conv.hex(chars[i])
System.print(" '%(c)' (%(hex)) is duplicated at positions %(i+1) and %(j+1).\n")
return
}
}
}
}
System.print(" All characters in the string are unique.\n")
}
var strings = [
"",
".",
"abcABC",
"XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X",
"hétérogénéité",
"🎆🎃🎇🎈",
"😍😀🙌💃😍🙌",
"🐠🐟🐡🦈🐬🐳🐋🐡"
]
for (s in strings) analyze.call(s)
- Output:
Analyzing "" which has a length of 0: All characters in the string are unique. Analyzing "." which has a length of 1: All characters in the string are unique. Analyzing "abcABC" which has a length of 6: All characters in the string are unique. Analyzing "XYZ ZYX" which has a length of 7: Not all characters in the string are unique. 'X' (0x58) is duplicated at positions 1 and 7. Analyzing "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" which has a length of 36: Not all characters in the string are unique. '0' (0x30) is duplicated at positions 10 and 25. Analyzing "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" which has a length of 39: Not all characters in the string are unique. '0' (0x30) is duplicated at positions 1 and 11. Analyzing "hétérogénéité" which has a length of 13: Not all characters in the string are unique. 'é' (0xe9) is duplicated at positions 2 and 4. Analyzing "🎆🎃🎇🎈" which has a length of 4: All characters in the string are unique. Analyzing "😍😀🙌💃😍🙌" which has a length of 6: Not all characters in the string are unique. '😍' (0x1f60d) is duplicated at positions 1 and 5. Analyzing "🐠🐟🐡🦈🐬🐳🐋🐡" which has a length of 8: Not all characters in the string are unique. '🐡' (0x1f421) is duplicated at positions 3 and 8.
XPL0
include xpllib; \contains StrLen function
proc StrUnique(S); \Show if string has unique chars
char S;
int L, I, J, K;
[L:= StrLen(S);
IntOut(0, L); Text(0, ": ^""); Text(0, S); ChOut(0, ^"); CrLf(0);
for I:= 0 to L-1 do
for J:= I+1 to L-1 do
[if S(I) = S(J) then
[ChOut(0, \tab\ 9);
for K:= 0 to I do ChOut(0, ^ );
ChOut(0, ^^);
for K:= 0 to J-I-2 do ChOut(0, ^ );
ChOut(0, ^^);
Text(0, " Duplicate character: ");
ChOut(0, S(I));
Text(0, ", hex ");
SetHexDigits(2);
HexOut(0, S(I));
CrLf(0);
return;
];
];
Text(0, " Unique, no duplicates"); CrLf(0);
];
[Text(0, "Length"); CrLf(0);
StrUnique("");
StrUnique(".");
StrUnique("abcABC");
StrUnique("XYZ ZYX");
StrUnique("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");
StrUnique("thequickbrownfoxjumps");
]
- Output:
Length 0: "" Unique, no duplicates 1: "." Unique, no duplicates 6: "abcABC" Unique, no duplicates 7: "XYZ ZYX" ^ ^ Duplicate character: X, hex 58 36: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" ^ ^ Duplicate character: 0, hex 30 21: "thequickbrownfoxjumps" ^ ^ Duplicate character: u, hex 75
Yabasic
sub caracteresunicos (cad$)
local lngt
lngt = len(cad$)
print "cadena = \"", cad$, "\", longitud = ", lngt
for i = 1 to lngt
for j = i + 1 to lngt
if mid$(cad$,i,1) = mid$(cad$,j,1) then
print " Primer duplicado en las posiciones ", i, " y ", j, ", caracter = \'", mid$(cad$,i,1), "\', valor hex = ", hex$(asc(mid$(cad$,i,1)))
print
return
end if
next j
next i
print " Todos los caracteres son unicos.\n"
end sub
caracteresunicos ("")
caracteresunicos (".")
caracteresunicos ("abcABC")
caracteresunicos ("XYZ ZYX")
caracteresunicos ("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")
- Output:
cadena = "", longitud = 0 Todos los caracteres son unicos. cadena = ".", longitud = 1 Todos los caracteres son unicos. cadena = "abcABC", longitud = 6 Todos los caracteres son unicos. cadena = "XYZ ZYX", longitud = 7 Primer duplicado en las posiciones 1 y 7, caracter = 'X', valor hex = 58 cadena = "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ", longitud = 36 Primer duplicado en las posiciones 10 y 25, caracter = '0', valor hex = 30
zkl
fcn stringUniqueness(str){ // Does not handle Unicode
sz,unique,uz,counts := str.len(), str.unique(), unique.len(), str.counts();
println("Length %d: \"%s\"".fmt(sz,str));
if(sz==uz or uz==1) println("\tAll characters are unique");
else // counts is (char,count, char,count, ...)
println("\tDuplicate: ",
counts.pump(List,Void.Read,fcn(str,c,n){
if(n>1){
is,z:=List(),-1; do(n){ is.append(z=str.find(c,z+1)) }
"'%s' (0x%x)[%s]".fmt(c,c.toAsc(),is.concat(","))
}
else Void.Skip
}.fp(str)).concat(", "));
}
testStrings:=T("", ".", "abcABC", "XYZ ZYX",
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
"01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X");
foreach s in (testStrings){ stringUniqueness(s) }
- Output:
Length 0: "" All characters are unique Length 1: "." All characters are unique Length 6: "abcABC" All characters are unique Length 7: "XYZ ZYX" Duplicate: 'X' (0x58)[0,6], 'Y' (0x59)[1,5], 'Z' (0x5a)[2,4] Length 36: "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" Duplicate: '0' (0x30)[9,24] Length 39: "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X" Duplicate: '0' (0x30)[0,10,25,37], 'X' (0x58)[34,38]