Determine if a string has all unique characters

From Rosetta Code
Revision as of 21:50, 11 November 2020 by Jjuanhdez (talk | contribs) (Determine if a string has all unique characters en FreeBASIC)
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



Ada

<lang 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; </lang>

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

<lang algol68>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</lang>

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

<lang applescript>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
               Template:K,
           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</lang>

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

AutoHotkey

<lang 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") }</lang> Examples:<lang AutoHotkey>test := ["",".","abcABC","XYZ ZYX","1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"] for i, v in test MsgBox % unique_characters(v) return</lang>

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

<lang AWK>

  1. 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) } </lang>

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

C

In interactive mode, strings with spaces have to be enclosed in double quotes ("") <lang C>

  1. include<stdbool.h>
  2. include<string.h>
  3. include<stdlib.h>
  4. 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;

} </lang> 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#

<lang csharp>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.")
               )
           );
       }
   }

}</lang>

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++

<lang cpp>#include <iostream>

  1. 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;

}</lang>

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

<lang 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))))))))

</lang>

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

<lang 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)</lang>

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++

<lang d>import std.stdio;

void uniqueCharacters(string str) {

   writefln("input: `%s`, length: %d", str, str.length);
   foreach (i; 0 .. str.length) {
       foreach (j; i + 1 .. str.length) {
           if (str[i] == str[j]) {
               writeln("String contains a repeated character.");
               writefln("Character '%c' (hex %x) occurs at positions %d and %d.", str[i], str[i], i + 1, j + 1);
               writeln;
               return;
           }
       }
   }
   writeln("String contains no repeated characters.");
   writeln;

}

void main() {

   uniqueCharacters("");
   uniqueCharacters(".");
   uniqueCharacters("abcABC");
   uniqueCharacters("XYZ ZYX");
   uniqueCharacters("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ");

}</lang>

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

<lang Delphi> program Determine_if_a_string_has_all_unique_characters;

{$APPTYPE CONSOLE}

uses

 System.SysUtils;

procedure string_has_repeated_character(str: string); var

 len, i, j: Integer;

begin

 len := length(str);
 Writeln('input: \', str, '\, length: ', len);
 for i := 1 to len - 1 do
 begin
   for j := i + 1 to len do
   begin
     if str[i] = str[j] then
     begin
       Writeln('String contains a repeated character.');
       Writeln('Character "', str[i], '" (hex ', ord(str[i]).ToHexString,
         ') occurs at positions ', i + 1, ' and ', j + 1, '.'#10);
       Exit;
     end;
   end;
 end;
 Writeln('String contains no repeated characters.' + sLineBreak);

end;

begin

 string_has_repeated_character();
 string_has_repeated_character('.');
 string_has_repeated_character('abcABC');
 string_has_repeated_character('XYZ ZYX');
 string_has_repeated_character('1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ');
 readln;

end.</lang>

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.

F#

<lang fsharp> // 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" </lang>

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

<lang 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</lang>

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.


FreeBASIC

<lang 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</lang>

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

<lang 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)
   }

}</lang>

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.

Haskell

<lang Haskell>import Data.List (groupBy, intersperse, sort, transpose) import Data.Char (ord, toUpper) 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 (\(x, _) (y, _) -> x == y) . 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"]</lang>
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:

<lang haskell>import Data.List (groupBy, intercalate, sortOn) import Control.Arrow ((&&&)) import Data.Function (on) import Numeric (showHex) import Data.Char (ord)

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:"
   ((++) <$> 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 =

 let rjust n c = drop . length <*> (replicate n c ++)
     w = maximum (length . xShow <$> xs)
 in unlines $
    s : fmap (((++) . rjust w ' ' . xShow) <*> ((" -> " ++) . fxShow . f)) xs</lang>
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: <lang haskell>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)

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)</lang>
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. <lang j> 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.

) </lang> 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 <lang j> 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.

) </lang>

Java

<lang 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);
   }

} </lang>

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

JavaScript

<lang 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();

})();</lang>

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: <lang javascript>(() => {

   '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();

})();</lang>

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

Julia

<lang 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é",

"🎆🎃🎇🎈", "😍😀🙌💃😍🙌", "🐠🐟🐡🦈🐬🐳🐋🐡", ])

</lang>

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

<lang scala>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)

}</lang>

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

<lang lua>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') </lang>

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

Nanoquery

<lang 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</lang>

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

Perl

<lang 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."
   }

}</lang>

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. <lang Phix>procedure all_uniq(sequence s)

   string msg = "all characters are unique"
   for i=1 to length(s) do
       integer si = s[i],
               r = find(si,s,i+1) -- (or maybe rfind(si,s,i-1))
       if r then
           msg = sprintf(`first duplicate character "%c"(#%02x) at positions %d and %d`,{si,si,i,r})
           exit
       end if
   end for
   printf(1,"\"%s\" (length %d): %s\n",{s,length(s),msg})

end procedure

constant tests = {"",".","abcABC","XYZ ZYX","1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"} for i=1 to length(tests) do all_uniq(tests[i]) end for</lang>

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"(#58) at positions 1 and 7
"1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ" (length 36): first duplicate character "0"(#30) at positions 10 and 25

PicoLisp

<lang 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")</lang>

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

<lang 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').</lang>

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:

<lang python>Determine if a string has all unique characters

from itertools import groupby


  1. 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)))


  1. TEST ----------------------------------------------------
  2. 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'
       ])
   )


  1. FORMATTING ----------------------------------------------
  1. fTable :: String -> (a -> String) ->
  2. (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
   )


  1. GENERIC -------------------------------------------------
  1. 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}


  1. 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}


  1. 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]


  1. fst :: (a, b) -> a

def fst(tpl):

   First member of a pair.
   return tpl[0]


  1. head :: [a] -> a

def head(xs):

   The first element of a non-empty list.
   return xs[0] if isinstance(xs, list) else next(xs)


  1. 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'))


  1. 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]))


  1. snd :: (a, b) -> b

def snd(tpl):

   Second member of a pair.
   return tpl[1]


  1. swap :: (a, b) -> (b, a)

def swap(tpl):

   The swapped components of a pair.
   return (tpl[1], tpl[0])


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
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:

<lang python>Determine if a string has all unique characters

from functools import reduce


  1. 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()


  1. And another alternative here would be to fuse the 1 < len(v)
  2. filtering, and the min() search for the earliest duplicate,
  3. down to a single `earliestDuplication` fold:
  1. 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()
   )


  1. TEST ----------------------------------------------------
  2. 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'
       ])
   )


  1. FORMATTING ----------------------------------------------
  1. fTable :: String -> (a -> String) ->
  2. (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
   )


  1. GENERIC -------------------------------------------------
  1. 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}


  1. 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}


  1. 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
   )


  1. head :: [a] -> a

def head(xs):

   The first element of a non-empty list.
   return xs[0] if isinstance(xs, list) else next(xs)


  1. 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'))


  1. snd :: (a, b) -> b

def snd(tpl):

   Second member of a pair.
   return tpl[1]


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
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.

<lang python>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') </lang>

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

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.

<lang perl6> -> $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',
   '🦋🙂👨‍👩‍👧‍👦🙄ΔΔ̂ 🦋Δ👍👨‍👩‍👧‍👦'</lang>
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

<lang 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.  */</lang>
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

<lang 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 </lang>

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'

Ruby

<lang 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 </lang>

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

<lang 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
           ),
       }
   }

} </lang>

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

<lang ruby>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

}</lang>

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

<lang 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é}

}

  1. 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]
   }

} </lang>

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#

<lang vbnet>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</lang>

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]

Wren

Translation of: Go
Library: Wren-fmt

<lang ecmascript>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)</lang>

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

<lang 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"); ]</lang>

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

<lang Yabasic>sub caracteresunicos (cad$)

   local lngt

lngt = len(cad$)

   print "cadena = \"", cad$, "\", longitud = ", lngt
   for i = 1 to lngt
       for j = i + 1 to lngt
           if mid$(cad$,i,1) = mid$(cad$,j,1) then
               print " Primer duplicado en las posiciones ", i, " y ", j, ", caracter = \'",  mid$(cad$,i,1), "\', valor hex = ", hex$(asc(mid$(cad$,i,1)))
               print
               return
           end if
       next j
   next i
   print " Todos los caracteres son unicos.\n"

end sub

caracteresunicos ("") caracteresunicos (".") caracteresunicos ("abcABC") caracteresunicos ("XYZ ZYX") caracteresunicos ("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ")</lang>

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

<lang 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(", ")); }</lang> <lang zkl>testStrings:=T("", ".", "abcABC", "XYZ ZYX",

  "1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ",
  "01234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ0X");

foreach s in (testStrings){ stringUniqueness(s) }</lang>

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]