Unique characters in each string
- Task
Given a list of strings, find the characters appearing exactly once in each string.
The result should be in alphabetical order.
Use the following list for this task:
["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
For this list, the result would be: 1 2 3 a b c
- Metrics
- Counting
- Word frequency
- Letter frequency
- Jewels and stones
- I before E except after C
- Bioinformatics/base count
- Count occurrences of a substring
- Count how many vowels and consonants occur in a string
- Remove/replace
- XXXX redacted
- Conjugate a Latin verb
- Remove vowels from a string
- String interpolation (included)
- Strip block comments
- Strip comments from a string
- Strip a set of characters from a string
- Strip whitespace from a string -- top and tail
- Strip control codes and extended characters from a string
- Anagrams/Derangements/shuffling
- Word wheel
- ABC problem
- Sattolo cycle
- Knuth shuffle
- Ordered words
- Superpermutation minimisation
- Textonyms (using a phone text pad)
- Anagrams
- Anagrams/Deranged anagrams
- Permutations/Derangements
- Find/Search/Determine
- ABC words
- Odd words
- Word ladder
- Semordnilap
- Word search
- Wordiff (game)
- String matching
- Tea cup rim text
- Alternade words
- Changeable words
- State name puzzle
- String comparison
- Unique characters
- Unique characters in each string
- Extract file extension
- Levenshtein distance
- Palindrome detection
- Common list elements
- Longest common suffix
- Longest common prefix
- Compare a list of strings
- Longest common substring
- Find common directory path
- Words from neighbour ones
- Change e letters to i in words
- Non-continuous subsequences
- Longest common subsequence
- Longest palindromic substrings
- Longest increasing subsequence
- Words containing "the" substring
- Sum of the digits of n is substring of n
- Determine if a string is numeric
- Determine if a string is collapsible
- Determine if a string is squeezable
- Determine if a string has all unique characters
- Determine if a string has all the same characters
- Longest substrings without repeating characters
- Find words which contains all the vowels
- Find words which contain the most consonants
- Find words which contains more than 3 vowels
- Find words whose first and last three letters are equal
- Find words with alternating vowels and consonants
- Formatting
- Substring
- Rep-string
- Word wrap
- String case
- Align columns
- Literals/String
- Repeat a string
- Brace expansion
- Brace expansion using ranges
- Reverse a string
- Phrase reversals
- Comma quibbling
- Special characters
- String concatenation
- Substring/Top and tail
- Commatizing numbers
- Reverse words in a string
- Suffixation of decimal numbers
- Long literals, with continuations
- Numerical and alphabetical suffixes
- Abbreviations, easy
- Abbreviations, simple
- Abbreviations, automatic
- Song lyrics/poems/Mad Libs/phrases
- Mad Libs
- Magic 8-ball
- 99 bottles of beer
- The Name Game (a song)
- The Old lady swallowed a fly
- The Twelve Days of Christmas
- Tokenize
- Text between
- Tokenize a string
- Word break problem
- Tokenize a string with escaping
- Split a character string based on change of character
- Sequences
11l
V LIST = [‘1a3c52debeffd’, ‘2b6178c97a938stf’, ‘3ycxdb1fgxa2yz’]
print(sorted(Set(Array(LIST.join(‘’))).filter(ch -> all(:LIST.map(w -> w.count(@ch) == 1)))))
- Output:
[1, 2, 3, a, b, c]
Action!
DEFINE MAX="128"
CHAR ARRAY uniq(MAX)
BYTE FUNC IsUnique(CHAR ARRAY s CHAR c)
BYTE count,i
count=0
FOR i=1 TO s(0)
DO
IF s(i)=c THEN
count==+1
IF count>1 THEN
RETURN (0)
FI
FI
OD
IF count=0 THEN
RETURN (0)
FI
RETURN (1)
PROC SetUnique(CHAR ARRAY s)
BYTE i,c
SetBlock(uniq,MAX,0)
FOR i=1 TO s(0)
DO
c=s(i)
IF IsUnique(s,c) THEN
uniq(c)=1
FI
OD
RETURN
PROC CheckUnique(CHAR ARRAY s)
BYTE i
FOR i=0 TO MAX-1
DO
IF uniq(i)=1 AND IsUnique(s,i)=0 THEN
uniq(i)=0
FI
OD
RETURN
PROC Main()
DEFINE PTR="CARD"
DEFINE CNT="3"
PTR ARRAY l(CNT)
INT i
l(0)="1a3c52debeffd"
l(1)="2b6178c97a938stf"
l(2)="3ycxdb1fgxa2yz"
SetUnique(l(0))
FOR i=1 TO CNT-1
DO
CheckUnique(l(i))
OD
FOR i=0 TO MAX-1
DO
IF uniq(i) THEN
Put(i) Put(32)
FI
OD
RETURN
- Output:
Screenshot from Atari 8-bit computer
1 2 3 a b c
Ada
with Ada.Text_Io;
procedure Unique_Characters is
type Occurence_Count is array (Character) of Natural;
type Occurence_List is array (Positive range <>) of Occurence_Count;
function Occurences (Item : String) return Occurence_Count is
Count : Occurence_Count := (others => 0);
begin
for C of Item loop
Count (C) := Count (C) + 1;
end loop;
return Count;
end Occurences;
procedure Put_Unique (List : Occurence_List) is
use Ada.Text_Io;
begin
for C in List (List'First)'Range loop
if (for all I in List'Range => List (I) (C) = 1) then
Put (C);
Put (' ');
end if;
end loop;
end Put_Unique;
begin
Put_Unique ((1 => Occurences ("1a3c52debeffd"),
2 => Occurences ("2b6178c97a938stf"),
3 => Occurences ("3ycxdb1fgxa2yz")));
end Unique_Characters;
- Output:
1 2 3 a b c
ALGOL 68
BEGIN # find the characters that occur once only in each element of a list #
# of stings #
OP UNIQUEINEACH = ( []STRING s )STRING:
IF INT required count = ( UPB s + 1 ) - LWB s;
required count < 1
THEN "" # the list is empty #
ELSE # non-empty list #
[ 0 : max abs char ]INT counts; # counts of used characters #
[ 0 : max abs char ]INT all;
FOR i FROM LWB all TO UPB all DO all[ i ] := 0 OD;
# count the occurances of the characters in the elements of s #
FOR i FROM LWB s TO UPB s DO
FOR j FROM LWB counts TO UPB counts DO counts[ j ] := 0 OD;
FOR j FROM LWB s[ i ] TO UPB s[ i ] DO
counts[ ABS s[ i ][ j ] ] +:= 1
OD;
FOR j FROM LWB counts TO UPB counts DO
# the character is unique in this string #
IF counts[ j ] = 1 THEN all[ j ] +:= 1 FI
OD
OD;
# construct a string of the characters that occur only once #
# in each string #
STRING result := "";
FOR i FROM LWB all TO UPB all DO
IF all[ i ] = required count THEN result +:= REPR i FI
OD;
result
FI; # UNIQUEINEACH #
# task test case #
STRING unique = UNIQUEINEACH []STRING( "1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz" );
FOR c FROM LWB unique TO UPB unique DO print( ( " ", unique[ c ] ) ) OD; print( ( newline ) )
END
- Output:
1 2 3 a b c
AppleScript
AppleScriptObjC
The filtering here is case sensitive, the sorting dependent on locale.
use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
on uniqueCharactersInEachString(listOfstrings)
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set countedSet to current application's class "NSCountedSet"'s setWithArray:(characters of (listOfstrings as text))
set AppleScript's text item delimiters to astid
set mutableSet to current application's class "NSMutableSet"'s setWithSet:(countedSet)
repeat with thisString in listOfstrings
tell mutableSet to intersectSet:(current application's class "NSSet"'s setWithArray:(thisString's characters))
tell countedSet to minusSet:(mutableSet)
end repeat
tell mutableSet to minusSet:(countedSet)
set sortDescriptor to current application's class "NSSortDescriptor"'s sortDescriptorWithKey:("self") ¬
ascending:(true) selector:("localizedStandardCompare:")
return (mutableSet's sortedArrayUsingDescriptors:({sortDescriptor})) as list
end uniqueCharactersInEachString
uniqueCharactersInEachString({"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"})
- Output:
{"1", "2", "3", "a", "b", "c"}
Core language only
This can be case-insensitive if required. (Just leave out the 'considering case' statement round the call to the handler). The requirement for AppleScript 2.3.1 is only for the 'use' command which loads the "Heap Sort" script. If "Heap Sort"'s instead loaded with the older 'load script' command or copied into the code, this will work on systems as far back as Mac OS X 10.5 (Leopard) and possibly earlier. Same output as above.
use AppleScript version "2.3.1" -- OS X 10.9 (Mavericks) or later
use sorter : script "Heap Sort" -- <https://www.rosettacode.org/wiki/Sorting_algorithms/Heapsort#AppleScript>
on uniqueCharactersInEachString(listOfStrings)
script o
property allCharacters : {}
property uniques : {}
on isInAllStrings(thisCharacter)
repeat with thisString in listOfStrings
if (thisCharacter is not in thisString) then return false
end repeat
return true
end isInAllStrings
end script
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set o's allCharacters to text items of (listOfStrings as text)
set AppleScript's text item delimiters to astid
set characterCount to (count o's allCharacters)
tell sorter to sort(o's allCharacters, 1, characterCount)
set i to 1
set stringCount to (count listOfStrings)
set currentCharacter to beginning of o's allCharacters
repeat with j from 2 to characterCount
set thisCharacter to item j of o's allCharacters
if (thisCharacter is not currentCharacter) then
if ((j - i = stringCount) and (o's isInAllStrings(currentCharacter))) then ¬
set end of o's uniques to currentCharacter
set i to j
set currentCharacter to thisCharacter
end if
end repeat
if ((j + 1 - i = stringCount) and (o's isInAllStrings(currentCharacter))) then ¬
set end of o's uniques to currentCharacter
return o's uniques
end uniqueCharactersInEachString
considering case
uniqueCharactersInEachString({"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"})
end considering
Arturo
arr: ["1a3c52debeffd" "2b6178c97a938stf" "3ycxdb1fgxa2yz"]
uniques: split first arr
loop arr 'str [
uniques: intersection uniques
select split str 'x ->
1 = size match str x
]
print sort uniques
- Output:
1 2 3 a b c
AWK
# syntax: GAWK -f UNIQUE_CHARACTERS_IN_EACH_STRING.AWK
#
# sorting:
# PROCINFO["sorted_in"] is used by GAWK
# SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
n = split("1a3c52debeffd,2b6178c97a938stf,3ycxdb1fgxa2yz",arr1,",")
for (i=1; i<=n; i++) {
str = arr1[i]
printf("%s\n",str)
total_c += leng = length(str)
for (j=1; j<=leng; j++) {
arr2[substr(str,j,1)][i]++
}
}
for (c in arr2) {
flag = 0
for (i=1; i<=n; i++) {
if (arr2[c][i] != 1) {
flag = 1
}
}
if (flag == 0) {
rec = sprintf("%s%s",rec,c)
}
}
printf("%d strings, %d characters, %d different, %d unique: %s\n",n,total_c,length(arr2),length(rec),rec)
exit(0)
}
- Output:
1a3c52debeffd 2b6178c97a938stf 3ycxdb1fgxa2yz 3 strings, 43 characters, 20 different, 6 unique: 123abc
BQN
(∧∘(∊/⊣)´ (∊∧∊⌾⌽)⊸/¨) "1a3c52debeffd"‿"2b6178c97a938stf"‿"3ycxdb1fgxa2yz"
- Output:
"123abc"
Delphi
var SA: array [0..2] of string = ('1a3c52debeffd', '2b6178c97a938stf', '3ycxdb1fgxa2yz');
function CharsAppearingOnce(S: string): string;
{Return all character that only occur once}
var SL: TStringList;
var I,Inx: integer;
begin
SL:=TStringList.Create;
try
{Store each character and store a count}
{of the number of occurances in the object}
for I:=1 to Length(S) do
begin
{Check to see if letter is already in list}
Inx:=SL.IndexOf(S[I]);
{Increment the count if it is, otherwise store it}
if Inx>=0 then SL.Objects[Inx]:=Pointer(Integer(SL.Objects[Inx])+1)
else SL.AddObject(S[I],Pointer(1));
end;
{Sort the list}
SL.Sort;
{Now return letters with a count of one}
Result:='';
for I:=0 to SL.Count-1 do
if integer(SL.Objects[I])<2 then Result:=Result+SL[I];
finally SL.Free; end;
end;
function CommonToAllStrs(SA: array of string): string;
{Get all the letters shared by all the strings in SA}
var I,J,Cnt: integer;
var S1: string;
var C: char;
begin
Result:='';
{Exit if empty array}
if Length(SA)<1 then exit;
{Get the first string}
S1:=SA[0];
for I:=1 to Length(S1) do
begin
{Character from 1st string }
C:=S1[I];
{Count # of occurences}
Cnt:=1;
for J:=1 to High(SA) do
if Pos(C,SA[J])>0 then Inc(Cnt);
{grab it if it appears in all other string}
if Cnt=Length(SA) then Result:=Result+C;
end;
end;
procedure ShowCharsAppearOnce(Memo: TMemo);
var I: integer;
var S: string;
var SS: array of string;
begin
SetLength(SS,0);
{Get all single appearance characters}
for I:=0 to High(SA) do
begin
SetLength(SS,Length(SS)+1);
SS[High(SS)]:=CharsAppearingOnce(SA[I]);
end;
{Get the ones shared by all string}
S:=CommonToAllStrs(SS);
Memo.Lines.Add(S);
end;
- Output:
123abc Elapsed Time: 1.238 ms.
EasyLang
len d[] 255
for s$ in [ "1a3c52debeffd" "2b6178c97a938stf" "3ycxdb1fgxa2yz" ]
for c$ in strchars s$
d[strcode c$] += 1
.
for i to 255
if d[i] = 1
d[i] = 0
else
d[i] = 2
.
.
.
for i to 255
if d[i] = 0
write strchar i & " "
.
.
- Output:
1 2 3 a b c
F#
// Unique characters in each string: Nigel Galloway. May 12th., 2021
let fN g=g|>Seq.countBy id|>Seq.filter(fun(_,n)->n=1)
let fUc g=g|>List.map fN|>Seq.concat|>Seq.countBy id|>Seq.filter(fun(_,n)->n=List.length g)|>Seq.map(fun((n,_),_)->n)|>Seq.sort
printfn "%s" (fUc ["1a3c52debeffd";"2b6178c97a938stf";"3ycxdb1fgxa2yz"]|>Array.ofSeq|>System.String)
- Output:
123abc
Factor
USING: io kernel sequences.interleaved sets sorting ;
{ "1a3c52debeffd" "2b6178c97a938sf" "3ycxdb1fgxa2yz" }
[ intersect-all ] [ [ duplicates ] gather without ] bi
natural-sort CHAR: space <interleaved> print
! How it works:
! intersect-all obtain elements present in every string -> "1a3c2bf"
! [ duplicates ] gather obtain elements that repeat within a single string -> "efd798xy"
! without from the first string, remove elements that are in the second -> "1a3c2b"
- Output:
1 2 3 a b c
FreeBASIC
function count_char( s as string, c as string ) as uinteger
'count occurrences of character c in string s
dim as integer i, r = 0
for i = 1 to len(s)
if mid(s,i,1) = c then r += 1
next i
return r
end function
dim as string*20 dat(1 to 3) = {"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"}
dim as string c, uniq
dim as integer i,j
for i = 1 to len(dat(1)) 'go through the first string
c = mid(dat(1),i,1)
for j = 1 to 3
if count_char(dat(j), c)<>1 then goto nexti 'contrary to popular belief, gotos are not evil
next j
for j = 1 to len(uniq)-1 'if it occurs once in every string
if mid(uniq,j+1,1)>c then exit for 'find where we need to put it in alphabetical order
next j
uniq = left(uniq,j)+c+right(uniq,len(uniq)-j) 'and insert it into its correct place
nexti:
next i
print uniq
- Output:
123abc
FutureBasic
local fn StringCharacterIsUnique( string as CFStringRef, chr as CFStringRef ) as BOOL
long count = 0, length = len(string)
CFRange range = fn StringRangeOfString( string, chr )
while ( range.location != NSNotFound )
count++
range.location++
range = fn StringRangeOfStringWithOptionsInRange( string, chr, 0, fn CFRangeMake(range.location,length-range.location) )
wend
end fn = (count == 1)
void local fn DoIt
CFArrayRef strings = @[@"1a3c52debeffd",@"2b6178c97a938stf",@"3ycxdb1fgxa2yz"]
CFStringRef chr
CFMutableArrayRef array = fn MutableArrayWithCapacity(0)
long i, j, count, length = len(strings[0])
for i = 0 to length - 1
chr = mid(strings[0],i,1)
if ( fn StringCharacterIsUnique( strings[0], chr ) )
count = 1
for j = 1 to len(strings) - 1
if ( fn StringCharacterIsUnique( strings[j], chr ) )
count++
end if
next
if ( count == len(strings) ) then MutableArrayAddObject( array, chr )
end if
next
MutableArraySortUsingSelector( array, @"compare:" )
print fn ArrayComponentsJoinedByString( array, @" " )
end fn
fn DoIt
HandleEvents
- Output:
1 2 3 a b c
Go
package main
import (
"fmt"
"sort"
)
func main() {
strings := []string{"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"}
u := make(map[rune]int)
for _, s := range strings {
m := make(map[rune]int)
for _, c := range s {
m[c]++
}
for k, v := range m {
if v == 1 {
u[k]++
}
}
}
var chars []rune
for k, v := range u {
if v == 3 {
chars = append(chars, k)
}
}
sort.Slice(chars, func(i, j int) bool { return chars[i] < chars[j] })
fmt.Println(string(chars))
}
- Output:
123abc
Haskell
import qualified Data.Map.Strict as M
import Data.Maybe (fromJust)
import qualified Data.Set as S
onceInEach :: [String] -> String
onceInEach [] = []
onceInEach ws@(x : xs) =
S.elems $
S.filter
((wordCount ==) . fromJust . flip M.lookup freq)
( foldr
(S.intersection . S.fromList)
(S.fromList x)
xs
)
where
wordCount = length ws
freq =
foldr
(flip (M.insertWith (+)) 1)
M.empty
(concat ws)
--------------------------- TEST -------------------------
main :: IO ()
main =
(putStrLn . onceInEach)
[ "1a3c52debeffd",
"2b6178c97a938stf",
"3ycxdb1fgxa2yz"
]
- Output:
123abc
J
/:~@> (e. # [)&.>/ (-. -.@~: # ])&.> '1a3c52debeffd';'2b6178c97a938stf';'3ycxdb1fgxa2yz'
123abc
JavaScript
(() => {
"use strict";
// --- CHARACTERS SEEN EXACTLY ONCE IN EACH STRING ---
// onceInEach :: [String] -> String
const onceInEach = ws =>
// Characters which occur exactly once
// in each word in a given list.
0 < ws.length ? (() => {
const
charFreqs = charCounts(ws.join("")),
charSet = s => new Set([...s]),
wordCount = ws.length;
return [
...ws.slice(1).reduceRight(
(a, x) => intersect(x)(
charSet(a)
),
charSet(ws[0])
)
]
.filter(c => wordCount === charFreqs[c])
.slice()
.sort()
.join("");
})() : "";
// ---------------------- TEST -----------------------
const main = () =>
onceInEach([
"1a3c52debeffd",
"2b6178c97a938stf",
"3ycxdb1fgxa2yz"
]);
// --------------------- GENERIC ---------------------
// charCounts :: String -> Dict Char Int
const charCounts = cs =>
// Dictionary of chars, with the
// frequency of each in cs.
[...cs].reduce(
(a, c) => Object.assign(
a, {
[c]: 1 + (a[c] || 0)
}
), {}
);
// intersect :: Set -> Set -> Set
const intersect = a =>
// The intersection of two sets.
b => new Set([...a].filter(i => b.has(i)));
// MAIN ---
return main();
})();
- Output:
123abc
jq
Works with gojq, the Go implementation of jq
Helper functions
# bag of words
def bow(stream):
reduce stream as $word ({}; .[($word|tostring)] += 1);
# input: an array of arrays that represent sets
# output: a stream of the items in all the input arrays
def intersections:
# intersection of (two) sets
# If a and b are sorted lists, and if all the elements respectively of a and b are distinct,
# then [a,b] | ios will emit the stream of elements in the set-intersection of a and b.
def ios:
.[0] as $a | .[1] as $b
| if 0 == ($a|length) or 0 == ($b|length) then empty
elif $a[0] == $b[0] then $a[0], ([$a[1:], $b[1:]] | ios)
elif $a[0] < $b[0] then [$a[1:], $b] | ios
else [$a, $b[1:]] | ios
end;
if length == 0 then empty
elif length == 1 then .[0][]
elif length == 2 then ios
elif (.[0]|length) == 0 then empty
else [.[0], [ .[1:] | intersections]] | ios
end;
The task
def once_in_each_string:
# convert each string to an array of the constituent characters
map((explode | map([.]|implode)))
# identify the singleton characters in each string; `keys` sorts the keys
| map( bow(.[]) | with_entries(select(.value==1)) | keys)
| intersections ;
["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
| [once_in_each_string]
- Output:
["1","2","3","a","b","c"]
Julia
list = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
onceineachstring(list) = filter(c -> all(w -> count(x -> x == c, w) == 1, list), (sort ∘ unique ∘ prod)(list))
println(onceineachstring(list))
- Output:
['1', '2', '3', 'a', 'b', 'c']
Mathematica /Wolfram Language
sets = Characters /@ {"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"};
uniques = Sort[Select[Tally[#], Last/*EqualTo[1]][[All, 1]]] & /@ sets;
Intersection @@ uniques
- Output:
{"1", "2", "3", "a", "b", "c"}
Nim
import strutils, tables
var result = AllChars
for str in ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]:
let charCount = str.toCountTable # Mapping char -> count.
var uniqueChars: set[char] # Set of unique chars.
for ch, count in charCount.pairs:
if count == 1: uniqueChars.incl ch
result = result * uniqueChars # Intersection.
echo result
- Output:
{'1', '2', '3', 'a', 'b', 'c'}
Nu
['1a3c52debeffd' '2b6178c97a938stf' '3ycxdb1fgxa2yz']
| each { split chars | uniq -u }
| reduce {|a b| $a ++ $b | uniq -d }
| sort
Pascal
program uniqueCharactersInEachString(output);
type
{ This “discriminates” the Extended Pascal (ISO 10206) schema }
{ data type `string` to hold a sequence of up to `16` characters. }
message = string(16);
characters = set of char;
{
\brief determine set of `char` values appear in a `string` once only
\param sample the `string` to inspect
\return a `set of char` appearing once in \param sample
}
{ In Extended Pascal `protected` denotes an immutable parameter. }
function uniqueCharacters(protected sample: message): characters;
var
{ Here, `sample.capacity` refers to `16`. }
i: 1..sample.capacity;
{ EP extension: `… value []` initializes variables to empty set value. }
firstOccurence, nonFirstOccurence: characters value [];
begin
for i := 1 to length(sample) do
begin
{ With sets, `+` denotes the union, `*` denotes the intersection. }
nonFirstOccurence := nonFirstOccurence + firstOccurence * [sample[i]];
firstOccurence := firstOccurence + [sample[i]]
end;
uniqueCharacters := firstOccurence - nonFirstOccurence
end;
{
\brief calls `uniqueCharacters` for several strings
\param sample the `array` of strings
\return characters appearing once in every \param sample member
}
function allUniqueCharacters(
{ This is a “conformant array parameter” as defined by }
{ ISO standard 7185 (level 1), and ISO standard 10206. }
protected sample: array[sampleMinimum..sampleMaximum: integer] of message
): characters;
var
{ `type of` is an Extended Pascal extension. }
i: type of sampleMinimum;
uniqueInEverySample: characters value [chr(0)..maxChar];
begin
for i := sampleMinimum to sampleMaximum do
begin
uniqueInEverySample := uniqueInEverySample * uniqueCharacters(sample[i])
end;
allUniqueCharacters := uniqueInEverySample
end;
{ === MAIN ============================================================= }
var
c: char;
sample: array[1..3] of message;
lonelyLetters: characters;
begin
sample[1] := '1a3c52debeffd';
sample[2] := '2b6178c97a938stf';
sample[3] := '3ycxdb1fgxa2yz';
lonelyLetters := allUniqueCharacters(sample);
{ If the compiler defines ascending evaluation order, in Extended Pascal }
{ it is also possible to write `for c in allUniqueCharacters(sample)`. }
{ However, the evaluation order (ascending/descending) is not defined by }
{ the ISO standard, but the compiler vendor. And note, while it’s }
{ guaranteed that `'A' < 'B'`, it is not guaranteed that `'a' < 'B'`. }
{ The following might (and actually on modern systems will) still produce }
{ a non-alphabetical listing. }
for c := chr(0) to maxChar do
begin
if c in lonelyLetters then
begin
{ The `:2` is a format specifier defining the width of the output. }
write(c:2)
end
end;
writeLn
end.
- Output:
1 2 3 a b c
Perl
#!/usr/bin/perl
use strict; # https://rosettacode.org/wiki/Unique_characters_in_each_string
use warnings;
my @strings = ("1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz");
my $chars = join "\n", @strings;
print "@{[ sort grep
$chars !~ /$_.*$_/ && # the 'only once in each string' test
@strings == $chars =~ s/$_//g, # the 'in every string' test
$chars =~ /./g ]}\n";
- Output:
1 2 3 a b c
Phix
include builtins\sets.e function once(integer ch, i, string s) integer l = length(s) return (i=1 or ch!=s[i-1]) and (i=l or ch!=s[i+1]) end function sequence set = {"1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"}, res = intersection(apply(true,filter,{apply(set,sort),once})) printf(1,"found %d unique common characters: %s\n",{length(res),res})
- Output:
found 6 unique common characters: 123abc
Picat
import ordset.
main =>
L = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"],
U = [new_ordset([C : C = 1 in counts(S)]) : S in L],
println(fold(intersection,U.first,U.tail)).
% Return a map of occurrences of each element in the list L
counts(L) = Map =>
Map = new_map(),
foreach(E in L)
Map.put(E,Map.get(E,0)+1)
end.
- Output:
123abc
PicoLisp
(de acc (V K N)
(if (assoc K (val V))
(inc (nth (cadr @) N))
(push V (list K (list 1 0 0))) ) )
(de un (Lst)
(let (Len (length Lst) D)
(for (I . Lst) (mapcar chop Lst)
(for L Lst
(acc 'D L I) ) )
(mapcar
car
(by
car
sort
(filter '((L) (fully =1 (cadr L))) D) ) ) ) )
(println
(un
(quote
"1a3c52debeffd"
"2b6178c97a938stf"
"3ycxdb1fgxa2yz" ) ) )
- Output:
("1" "2" "3" "a" "b" "c")
Python
LIST = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
print(sorted([ch for ch in set([c for c in ''.join(LIST)]) if all(w.count(ch) == 1 for w in LIST)]))
- Output:
['1', '2', '3', 'a', 'b', 'c']
Or, avoiding intermediate lists.
LIST = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
print(sorted(ch for ch in set("".join(LIST)) if all(w.count(ch) == 1 for w in LIST)))
- Output:
['1', '2', '3', 'a', 'b', 'c']
We can also avoid concatenating all strings in the input list.
from itertools import chain
LIST = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
print(
sorted(
ch
for ch in set(chain.from_iterable(LIST))
if all(w.count(ch) == 1 for w in LIST)
)
)
- Output:
['1', '2', '3', 'a', 'b', 'c']
Or, avoid calling count()
for every distinct character in every string in the input list.
from collections import Counter
LIST = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
print(
sorted(
set.intersection(
*(
set(char for char, count in counts.items() if count == 1)
for counts in (Counter(s) for s in LIST)
)
)
)
)
- Output:
['1', '2', '3', 'a', 'b', 'c']
Quackery
[ 0 128 of
swap witheach
[ 2dup peek
1+ unrot poke ] ] is countchars ( $ --> [ )
[ [] swap witheach
[ 1 = join ] ] is justones ( [ --> [ )
[ witheach
[ over i^ peek +
swap i^ poke ] ] is addnests ( [ [ --> [ )
[ [] swap witheach
[ 3 = if [ i^ join ] ] ] is threesfound ( [ --> $ )
$ "1a3c52debeffd" nested
$ "2b6178c97a938stf" nested join
$ "3ycxdb1fgxa2yz" nested join
witheach [ countchars justones ]
2 times addnests
threesfound
witheach [ emit sp ]
- Output:
1 2 3 a b c
Raku
my $strings = <1a3c52debeffd 2b6178c97a938stf 3ycxdb1fgxa2yz>;
put sort keys [∩] $strings.map: *.comb.Bag.grep: *.value == 1
- Output:
1 2 3 a b c
REXX
This REXX program doesn't assume ASCII (or any other) order. This example was run on an ASCII machine.
If this REXX program is run on an ASCII machine, it will use the ASCII order of characters, in this case,
decimal digits, uppercase Latin letters, and then lowercase Latin letters, with other characters interspersed.
On an EBCDIC machine, the order would be lowercase Latin letters, uppercase Latin letters, and then the
decimal digits, with other characters interspersed.
On an EBCDIC machine, the lowercase letters and the uppercase letters aren't contiguous.
/*REXX pgm finds and shows characters that are unique in each string and once only. */
parse arg $ /*obtain optional arguments from the CL*/
if $='' | $="," then $= '1a3c52debeffd' "2b6178c97a938stf" '3ycxdb1fgxa2yz'
if $='' then do; say "***error*** no lists were specified."; exit 13; end
#= words($); $$= /*#: # words in $; $$: $ with no blanks*/
do i=1 for #; !.i= word($, i) /*for speed, build a list of words in $*/
$$= $$ || !.i /*build a list of all the strings. */
end /*i*/
@= /*will be a list of all unique chars. */
do j=0 for 256; x= d2c(j) /*process all the possible characters. */
if pos(x, $$)==0 then iterate /*Char not found in any string in $ ? */
do k=1 for #; _= pos(x, !.k) /*examine each string in the $ list. */
if _==0 then iterate j /*Character not found? Then skip it. */
if pos(x, !.k, _+1)>0 then iterate j /* " is a dup? " " " */
end /*k*/
@= @ x /*append a character, append it to list*/
end /*j*/ /*stick a fork in it, we're all done. */
@@= space(@, 0); L= length(@@) /*elided superfluous blanks; get length*/
if @@=='' then @= " (none)" /*if none were found, pretty up message*/
if L==0 then L= "no" /*do the same thing for the # of chars.*/
say 'unique characters are: ' @ /*display the unique characters found. */
say
say 'Found ' L " unique characters." /*display the # of unique chars found. */
- output when using the default input:
unique characters are: 1 2 3 a b c Found 6 unique characters.
Ring
see "working..." + nl
see "Unique characters in each string are:" + nl
row = 0
str = ""
cList = []
uniqueChars = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
lenChars = len(uniqueChars)
for n = 1 to lenChars
str = str + uniqueChars[n]
next
for n = 1 to len(str)
flag = 1
for m = 1 to lenChars
cnt = count(uniqueChars[m],str[n])
if cnt != 1
flag = 0
exit
ok
next
if flag = 1
ind = find(cList,str[n])
if ind = 0
add(cList,str[n])
ok
ok
next
cList = sort(cList)
for n = 1 to len(cList)
row = row + 1
see "" + cList[n] + " "
next
see nl
see "Found " + row + " unique characters in each string" + nl
see "done..." + nl
func count(cString,dString)
sum = 0
while substr(cString,dString) > 0
sum++
cString = substr(cString,substr(cString,dString)+len(string(sum)))
end
return sum
- Output:
working... Unique characters in each string are: 1 2 3 a b c Found 6 unique characters in each string done...
RPL
≪ → word char ≪ 0 1 word SIZE FOR j word j DUP SUB char == + NEXT ≫ ≫ 'OCCHAR' STO ≪ "" → words char ≪ { } words 1 GET 1 OVER SIZE FOR j DUP j DUP SUB 'char' STO words 1 ≪ char OCCHAR ≫ DOLIST IF ΠLIST 1 == THEN SWAP char + SWAP END NEXT DROP SORT ≫ ≫ 'UNICHARS' STO
{ "1a3c52debeffd" "2b6178c97a938stf" "3ycxdb1fgxa2yz" } UNICHARS
- Output:
1: { "1" "2" "3" "a" "b" "c" }
Ruby
arr = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
uniqs_in_str = arr.map{|str| str.chars.tally.filter_map{|char, count| char if count == 1} }
puts uniqs_in_str.inject(&:intersection).sort.join(" ")
- Output:
1 2 3 a b c
Rust
fn main() {
let list = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"];
let mut charset: Vec<char> = list.join("").chars().collect();
charset.sort();
charset.dedup();
println!(
"{:?}",
charset
.iter()
.filter(|c| {
list.iter()
.filter(|s| s.chars().filter(|x| &x == c).count() == 1)
.count()
== list.len()
})
.collect::<Vec<&char>>()
);
}
- Output:
['1', '2', '3', 'a', 'b', 'c']
Transd
#lang transd
MainModule: {
_start: (lambda locals: pos Position<String>() b Bool()
(for c in (sort "1a3c52debeffd") do (= b true)
(for str in ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"] do
(= pos (find str c))
(if (or (is-end pos) (find Range(in: str (+ (get-idx pos) 1) -0) c))
(= b false)))
(if b (textout c " "))
) )
}
- Output:
1 2 3 a b c
Uiua
One-liner
UCIES ← ⊙◌ ⊏⍏. ▽≡/◇× ≡(⍚/+) ⊞=⟜: (°□⊃(⊢|↘1)). ⍚◴
UCIES {"champions" "pitchman" "morphic"}
- Output:
"chimp"
Comparison
Compares two approaches:
- first (SCL) looks for the singleton characters in each string, then finds their intersection.
- second (STC) finds the common characters across all the strings and then checks to see which are always singletons.
The first approach is twice as fast for a large number of strings.
# Find common singletons across strings
# https://rosettacode.org/wiki/Unique_characters_in_each_string
# First, build a large array of strings (very cheaply)
⊢↯1_1000{"1a3c52debeffd" "2b6178c97a938stf" "3ycxdb1fgxa2yz"}
Lone ← ▽⊸(/+▽⊸(=1/+)⊞=.)
Common ← ▽/↥⊸⊞=
SCL ← ⊏⊸⍏/◇Common∵⍚Lone
Trim ← ▽⊸(/↧⊞≠)▽⊸(¬◰)
STC ← ⊏⊸⍏∧◇Trim⟜/◇Common
⊃(⍜nowSCL|⍜nowSTC)
- Output:
"123abc" 0.16899…986903 "123abc" 0.29199…94616
Wren
import "./seq" for Lst
import "./sort" for Sort
var strings = ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]
var uniqueChars = []
for (s in strings) {
var u = Lst.individuals(s.toList).where { |l| l[1] == 1 }.map { |l| l[0] }
uniqueChars.addAll(u)
}
var n = strings.count
uniqueChars = Lst.individuals(uniqueChars).where { |l| l[1] == n }.map { |l| l[0] }.toList
Sort.insertion(uniqueChars)
System.print("Found %(uniqueChars.count) unique character(s) common to each string, namely:")
System.print(uniqueChars.join(" "))
- Output:
Found 6 unique character(s) common to each string, namely: 1 2 3 a b c
V (Vlang)
fn main() {
strings := ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"]!
mut u := map[rune]int{}
for s in strings {
mut m := map[rune]int{}
for c in s {
m[c]++
}
for k, v in m {
if v == 1 {
u[k]++
}
}
}
mut chars := []rune{}
for k, v in u {
if v == 3 {
chars << k
}
}
chars.sort()
println(chars.string())
}
- Output:
123abc
XPL0
char List, Counter(128), Unique(3, 128);
int Char, I, J;
string 0; \use null-terminated string convention
[List:= ["1a3c52debeffd", "2b6178c97a938stf", "3ycxdb1fgxa2yz"];
for I:= 0 to 2 do \each string in List
[for J:= 0 to 127 do Counter(J):= 0;
J:= 0; \count each character in string
repeat Char:= List(I, J);
J:= J+1;
Counter(Char):= Counter(Char)+1;
until Char = 0; \terminator
for J:= 0 to 127 do
Unique(I, J):= Counter(J) = 1;
];
for Char:= 1 to 127 do \skip 0 terminator
if Unique(0, Char) & Unique(1, Char) & Unique(2, Char) then
[ChOut(0, Char); ChOut(0, ^ )];
]
- Output:
1 2 3 a b c
Yabasic
sub count_char(s$, c$)
local i, r
r = 0
for i = 1 to len(s$)
if mid$(s$,i,1) = c$ then r = r + 1 : fi
next i
return r
end sub
dim dat$(2)
dat$(0) = "1a3c52debeffd"
dat$(1) = "2b6178c97a938stf"
dat$(2) = "3ycxdb1fgxa2yz"
for i = 1 to len(dat$(1))
c$ = mid$(dat$(1), i, 1)
for j = 0 to 2
if count_char(dat$(j), c$) <> 1 then goto nexti : fi
next j
for j = 0 to len(uniq$)-1
if mid$(uniq$, j+1, 1) > c$ then break : fi
next j
uniq$ = left$(uniq$, j) + c$ + right$(uniq$, len(uniq$)-j)
label nexti
next i
print uniq$
end
- Output:
123abc
- Draft Programming Tasks
- 11l
- Action!
- Ada
- ALGOL 68
- AppleScript
- Arturo
- AWK
- BQN
- Delphi
- SysUtils,StdCtrls
- EasyLang
- F Sharp
- Factor
- FreeBASIC
- FutureBasic
- Go
- Haskell
- J
- JavaScript
- Jq
- Julia
- Mathematica
- Wolfram Language
- Nim
- Nu
- Pascal
- Perl
- Phix
- Picat
- PicoLisp
- Python
- Quackery
- Raku
- REXX
- Ring
- RPL
- Ruby
- Rust
- Transd
- Uiua
- Wren
- Wren-seq
- Wren-sort
- V (Vlang)
- XPL0
- Yabasic