Determine if a string has all unique characters

From Rosetta Code
Revision as of 16:23, 27 May 2023 by PSNOW123 (talk | contribs) (Altered variable identifier.)
Task
Determine if a string has all unique characters
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Given a character string   (which may be empty, or have a length of zero characters):

  •   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.

Other tasks related to string operations:
Metrics
Counting
Remove/replace
Anagrams/Derangements/shuffling
Find/Search/Determine
Formatting
Song lyrics/poems/Mad Libs/phrases
Tokenize
Sequences



11l

Translation of: Kotlin
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:

Translation of: Haskell
Translation of: Python
Translation of: JavaScript
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"+¨1026)16{𝕗|⌊÷𝕗(1+·𝕗1⌈⊢)}
 
{
  𝕊 str:
  rCheck 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

Translation of: C++
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

Translation of: Cpp
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

Translation of: Java
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

Translation of: Java
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)

Works with: Rakudo version 2020.08.1

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

Works with: Halcyon Calc version 4.2.7
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

Translation of: C#
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)

Translation of: Go
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

Translation of: Go
Library: Wren-fmt
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

Translation of: FreeBASIC
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]