Determine if a string has all unique characters: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎Using regular expression: noted Python version)
Line 2,403: Line 2,403:
The second part of the pattern uses the '*?' match qualifier, which makes the match "lazy" or "reluctant". '.*' instead of '.*?'
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.
would have matched the substring "cocc" instead of "coc" in the first example below.
Tested with Python 3.7.


<lang python>import re
<lang python>import re

Revision as of 00:04, 22 June 2020

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.


Related tasks



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#

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

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.

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.

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

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 2019.07.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.perl} (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

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.

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

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]