Determine if a string has all unique characters: Difference between revisions
Line 1,607: | Line 1,607: | ||
🐠🐟🐡🦈🐬🐳🐋🐡 8 no 🐡 (1f421) 3 8 |
🐠🐟🐡🦈🐬🐳🐋🐡 8 no 🐡 (1f421) 3 8 |
||
</pre> |
</pre> |
||
=={{header|Kotlin}}== |
|||
{{trans|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> |
|||
{{out}} |
|||
<pre>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</pre> |
|||
=={{header|Nanoquery}}== |
=={{header|Nanoquery}}== |
Revision as of 00:01, 14 March 2020
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
AppleScript
Following AppleScript's convention of one-based indices:
<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>
- 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>
- include<stdbool.h>
- include<string.h>
- include<stdlib.h>
- include<stdio.h>
typedef struct positionList{
int position; struct positionList *next;
}positionList;
typedef struct letterList{
char letter; int repititions; positionList* positions; struct letterList *next;
}letterList;
letterList* letterSet; bool duplicatesFound = false;
void checkAndUpdateLetterList(char c,int pos){
bool letterOccurs = false; letterList *letterIterator,*newLetter; positionList *positionIterator,*newPosition;
if(letterSet==NULL){ letterSet = (letterList*)malloc(sizeof(letterList)); letterSet->letter = c; letterSet->repititions = 0;
letterSet->positions = (positionList*)malloc(sizeof(positionList)); letterSet->positions->position = pos; letterSet->positions->next = NULL;
letterSet->next = NULL; }
else{ letterIterator = letterSet;
while(letterIterator!=NULL){ if(letterIterator->letter==c){ letterOccurs = true; duplicatesFound = true;
letterIterator->repititions++; positionIterator = letterIterator->positions;
while(positionIterator->next!=NULL) positionIterator = positionIterator->next; newPosition = (positionList*)malloc(sizeof(positionList)); newPosition->position = pos; newPosition->next = NULL;
positionIterator->next = newPosition; } if(letterOccurs==false && letterIterator->next==NULL) break; else letterIterator = letterIterator->next; }
if(letterOccurs==false){ newLetter = (letterList*)malloc(sizeof(letterList)); newLetter->letter = c;
newLetter->repititions = 0;
newLetter->positions = (positionList*)malloc(sizeof(positionList)); newLetter->positions->position = pos; newLetter->positions->next = NULL;
newLetter->next = NULL;
letterIterator->next = newLetter; } }
}
void printLetterList(){
positionList* positionIterator; letterList* letterIterator = letterSet;
while(letterIterator!=NULL){ if(letterIterator->repititions>0){ printf("\n'%c' (0x%x) at positions :",letterIterator->letter,letterIterator->letter);
positionIterator = letterIterator->positions;
while(positionIterator!=NULL){ printf("%3d",positionIterator->position + 1); positionIterator = positionIterator->next; } }
letterIterator = letterIterator->next; } printf("\n");
}
int main(int argc,char** argv) {
int i,len; if(argc>2){ printf("Usage : %s <Test string>\n",argv[0]); return 0; }
if(argc==1||strlen(argv[1])==1){ printf("\"%s\" - Length %d - Contains only unique characters.\n",argc==1?"":argv[1],argc==1?0:1); return 0; }
len = strlen(argv[1]);
for(i=0;i<len;i++){ checkAndUpdateLetterList(argv[1][i],i); }
printf("\"%s\" - Length %d - %s",argv[1],len,duplicatesFound==false?"Contains only unique characters.\n":"Contains the following duplicate characters :");
if(duplicatesFound==true) printLetterList(); return 0;
} </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 cpp>#include <iostream>
- include <string>
void string_has_repeated_character(const std::string& str) {
size_t len = str.length(); std::cout << "input: \"" << str << "\", length: " << len << '\n'; for (size_t i = 0; i < len; ++i) { for (size_t j = i + 1; j < len; ++j) { if (str[i] == str[j]) { std::cout << "String contains a repeated character.\n"; std::cout << "Character '" << str[i] << "' (hex " << std::hex << static_cast<unsigned int>(str[i]) << ") occurs at positions " << std::dec << i + 1 << " and " << j + 1 << ".\n\n"; return; } } } std::cout << "String contains no repeated characters.\n\n";
}
int main() {
string_has_repeated_character(""); string_has_repeated_character("."); string_has_repeated_character("abcABC"); string_has_repeated_character("XYZ ZYX"); string_has_repeated_character("1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ"); return 0;
}</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.
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]
D
<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.
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
<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
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
Perl 6
Perl 6 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
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
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
- duplicatedCharIndices :: String -> Maybe (Char, [Int])
def duplicatedCharIndices(s):
Just the first duplicated character, and the indices of its occurrence, or Nothing if there are no duplications. def go(xs): if 1 < len(xs): duplicates = list(filter(lambda kv: 1 < len(kv[1]), [ (k, list(v)) for k, v in groupby( sorted(xs, key=swap), key=snd ) ])) return Just(second(fmap(fst))( sorted( duplicates, key=lambda kv: kv[1][0] )[0] )) if duplicates else Nothing() else: return Nothing() return go(list(enumerate(s)))
- TEST ----------------------------------------------------
- main :: IO ()
def main():
Test over various strings.
def showSample(s): return repr(s) + ' (' + str(len(s)) + ')'
def showDuplicate(cix): c, ix = cix return repr(c) + ( ' (' + hex(ord(c)) + ') at ' + repr(ix) )
print( fTable('First duplicated character, if any:')( showSample )(maybe('None')(showDuplicate))(duplicatedCharIndices)([ , '.', 'abcABC', 'XYZ ZYX', '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' ]) )
- FORMATTING ----------------------------------------------
- fTable :: String -> (a -> String) ->
- (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
Heading -> x display function -> fx display function -> f -> xs -> tabular string. def go(xShow, fxShow, f, xs): ys = [xShow(x) for x in xs] w = max(map(len, ys)) return s + '\n' + '\n'.join(map( lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)), xs, ys )) return lambda xShow: lambda fxShow: lambda f: lambda xs: go( xShow, fxShow, f, xs )
- GENERIC -------------------------------------------------
- Just :: a -> Maybe a
def Just(x):
Constructor for an inhabited Maybe (option type) value. Wrapper containing the result of a computation. return {'type': 'Maybe', 'Nothing': False, 'Just': x}
- Nothing :: Maybe a
def Nothing():
Constructor for an empty Maybe (option type) value. Empty wrapper returned where a computation is not possible. return {'type': 'Maybe', 'Nothing': True}
- fmap :: (a -> b) -> [a] -> [b]
def fmap(f):
fmap over a list. f lifted to a function over a list. return lambda xs: [f(x) for x in xs]
- fst :: (a, b) -> a
def fst(tpl):
First member of a pair. return tpl[0]
- head :: [a] -> a
def head(xs):
The first element of a non-empty list. return xs[0] if isinstance(xs, list) else next(xs)
- maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
Either the default value v, if m is Nothing, or the application of f to x, where m is Just(x). return lambda f: lambda m: v if ( None is m or m.get('Nothing') ) else f(m.get('Just'))
- second :: (a -> b) -> ((c, a) -> (c, b))
def second(f):
A simple function lifted to a function over a tuple, with f applied only to the second of two values. return lambda xy: (xy[0], f(xy[1]))
- snd :: (a, b) -> b
def snd(tpl):
Second member of a pair. return tpl[1]
- swap :: (a, b) -> (b, a)
def swap(tpl):
The swapped components of a pair. return (tpl[1], tpl[0])
- MAIN ---
if __name__ == '__main__':
main()</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
- duplicatedCharIndices :: String -> Maybe (Char, [Int])
def duplicatedCharIndices(s):
Just the first duplicated character, and the indices of its occurrence, or Nothing if there are no duplications. def go(dct, ic): i, c = ic return dict( dct, **{c: dct[c] + [i] if c in dct else [i]} ) duplicates = [ (k, v) for (k, v) in reduce(go, enumerate(s), {}).items() if 1 < len(v) ] return Just( min(duplicates, key=compose(head, snd)) ) if duplicates else Nothing()
- And another alternative here would be to fuse the 1 < len(v)
- filtering, and the min() search for the earliest duplicate,
- down to a single `earliestDuplication` fold:
- duplicatedCharIndices_ :: String -> Maybe (Char, [Int])
def duplicatedCharIndices_(s):
Just the first duplicated character, and the indices of its occurrence, or Nothing if there are no duplications. def positionRecord(dct, ic): i, c = ic return dict( dct, **{c: dct[c] + [i] if c in dct else [i]} )
def earliestDuplication(sofar, charPosns): c, indices = charPosns return ( maybe(Just((c, indices)))( lambda kxs: Just((c, indices)) if ( # Earlier duplication ? indices[0] < kxs[1][0] ) else sofar )(sofar) ) if 1 < len(indices) else sofar
return reduce( earliestDuplication, reduce( positionRecord, enumerate(s), {} ).items(), Nothing() )
- TEST ----------------------------------------------------
- main :: IO ()
def main():
Test over various strings.
def showSample(s): return repr(s) + ' (' + str(len(s)) + ')'
def showDuplicate(cix): c, ix = cix return repr(c) + ( ' (' + hex(ord(c)) + ') at ' + repr(ix) )
print( fTable('First duplicated character, if any:')( showSample )(maybe('None')(showDuplicate))(duplicatedCharIndices_)([ , '.', 'abcABC', 'XYZ ZYX', '1234567890ABCDEFGHIJKLMN0PQRSTUVWXYZ' ]) )
- FORMATTING ----------------------------------------------
- fTable :: String -> (a -> String) ->
- (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
Heading -> x display function -> fx display function -> f -> xs -> tabular string. def go(xShow, fxShow, f, xs): ys = [xShow(x) for x in xs] w = max(map(len, ys)) return s + '\n' + '\n'.join(map( lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)), xs, ys )) return lambda xShow: lambda fxShow: lambda f: lambda xs: go( xShow, fxShow, f, xs )
- GENERIC -------------------------------------------------
- Just :: a -> Maybe a
def Just(x):
Constructor for an inhabited Maybe (option type) value. Wrapper containing the result of a computation. return {'type': 'Maybe', 'Nothing': False, 'Just': x}
- Nothing :: Maybe a
def Nothing():
Constructor for an empty Maybe (option type) value. Empty wrapper returned where a computation is not possible. return {'type': 'Maybe', 'Nothing': True}
- compose :: ((a -> a), ...) -> (a -> a)
def compose(*fs):
Composition, from right to left, of a series of functions. return lambda x: reduce( lambda a, f: f(a), fs[::-1], x )
- head :: [a] -> a
def head(xs):
The first element of a non-empty list. return xs[0] if isinstance(xs, list) else next(xs)
- maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
Either the default value v, if m is Nothing, or the application of f to x, where m is Just(x). return lambda f: lambda m: v if ( None is m or m.get('Nothing') ) else f(m.get('Just'))
- snd :: (a, b) -> b
def snd(tpl):
Second member of a pair. return tpl[1]
- MAIN ---
if __name__ == '__main__':
main()</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]
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é}
}
- 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.
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]