Unique characters

From Rosetta Code
Unique characters is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Given a list of strings,   find characters appearing only in one string and once only.

The result should be given in alphabetical order.


Use the following list for this task:

        ["133252abcdeeffd",  "a6789798st",  "yxcdfgxcyz"]


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



8080 Assembly

<lang 8080asm>puts: equ 9 ; CP/M print syscall TERM: equ '$' ; CP/M string terminator org 100h jmp demo ;;; Given a list of strings, find characters appearing only ;;; in one string and once only. ;;; Input: DE = list of strings, BC = start of output unique: xra a ; Zero out the workspace mvi h,upage mov l,a uzbuf: mov m,a inr l jnz uzbuf push d ustr: pop h mov e,m ; Load next string pointer inx h mov d,m inx h mov a,d ; End of list? ora e jz uclat ; Then go find the uniques push h ; Otherwise, keep list pointer mvi h,upage uchar: ldax d ; Get character cpi TERM ; Done? jz ustr ; Then do next string mov l,a ; Otherwise, count the character inr m inx d ; Next character jmp uchar uclat: lxi h,upage*256 ; Start of page utst: dcr m ; Is this character included? jnz uskip mov a,l ; If so add it to the output stax b inx b uskip: inr l ; Try next character jnz utst mvi a,TERM ; CP/M string terminator stax b ret ;;; Demo code demo: lxi b,outbuf ; Set BC to location of output buffe lxi d,list ; Set DE to the list of strings call unique ; Call the code mvi c,puts ; Print the result lxi d,outbuf jmp 5 ;;; List of strings list: dw str1, str2, str3, 0 str1: db '133252abcdeeffd', TERM str2: db 'a6789798st', TERM str3: db 'yxcdfgxcyz', TERM ;;; Memory upage: equ ($/256)+1 ; Workspace for 'unique' outbuf: equ (upage+1)*256 ; Output </lang>

Output:
156bgstz


8086 Assembly

<lang asm> cpu 8086 org 100h puts: equ 9 ; MS-DOS syscall to print a string TERM: equ '$' ; String terminator section .text jmp demo ;;; Given a list of strings, find characters appearing ;;; only in one string and once only. ;;; Input: BX = list of strings, DX = start of output ;;; Assuming DS = ES unique: mov cx,128 ; Zero out array mov di,uniqws xor ax,ax rep stosw .str: mov si,[bx] ; Get next string inc bx inc bx test si,si ; Done? jz .fltr ; Then start writing to output .char: lodsb ; Read character cmp al,TERM ; Done? je .str ; Then get next string mov di,ax ; Otherwise, count the character inc byte [di+uniqws] jmp .char .fltr: mov di,dx xor bx,bx .fchr: dec byte [bx+uniqws] jnz .skip ; Character occurs once? mov al,bl ; If so, write it stosb .skip: inc bl ; Any more? jnz .fchr mov [di],byte TERM ; Terminate string ret ;;; Demo code demo: mov bx,list ; Find unique characters mov dx,outbuf call unique mov ah,puts ; Print result mov dx,outbuf int 21h ret section .data list: dw .s1, .s2, .s3, 0 .s1: db '133252abcdeeffd', TERM .s2: db 'a6789798st', TERM .s3: db 'yxcdfgxcyz', TERM section .bss uniqws: resb 256 outbuf: resb 256</lang>

Output:
156bgstz

AppleScript

AppleScriptObjC

The filtering here is case sensitive, the sorting dependent on locale.

<lang applescript>on uniqueCharacters(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:((listOfStrings as text)'s characters)
   set AppleScript's text item delimiters to astid
   set mutableSet to current application's class "NSMutableSet"'s setWithSet:(countedSet)
   tell countedSet to minusSet:(mutableSet)
   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 uniqueCharacters</lang>

Output:

<lang applescript>{"1", "5", "6", "b", "g", "s", "t", "z"}</lang>

Core language only

This isn't quite as fast as the ASObjC solution above, but it can be case-insensitive if required. (Simply leave out the 'considering case' statement round the call to the handler). The requirement for AppleScript 2.3.1 is just for the 'use' command which loads the "Heap Sort" script. If "Heap Sort"'s loaded differently or compiled directly into the code, this script will work on systems at least as far back as Mac OS X 10.5 (Leopard) and possibly earlier. Same output as above.

<lang applescript>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 uniqueCharacters(listOfStrings)

   script o
       property allCharacters : {}
       property uniques : {}
   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 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 = 1) then set end of o's uniques to currentCharacter
           set i to j
           set currentCharacter to thisCharacter
       end if
   end repeat
   if (i = j) then set end of o's uniques to currentCharacter
   
   return o's uniques

end uniqueCharacters

considering case

   return uniqueCharacters({"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"})

end considering</lang>


Functional

Composing a solution from existing generic primitives, for speed of drafting and refactoring, and for high levels of code reuse.

<lang applescript>use framework "Foundation"



UNIQUE CHARACTERS -------------------

-- uniques :: [String] -> String on uniques(xs)

   script single
       on |λ|(x)
           if 1 = length of x then
               item 1 of x
           else
               {}
           end if
       end |λ|
   end script
   
   concatMap(single, ¬
       group(sort(concatMap(my chars, xs))))

end uniques



TEST -------------------------

on run

   uniques({"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"})
   
   --> {"1", "5", "6", "b", "g", "s", "t", "z"}

end run



GENERIC ------------------------

-- chars :: String -> [Char] on chars(s)

   characters of s

end chars


-- 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
   if {text, string} contains class of xs then
       acc as text
   else
       acc
   end if

end concatMap


-- eq (==) :: Eq a => a -> a -> Bool on eq(a, b)

   a = b

end eq


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


-- group :: Eq a => [a] -> a on group(xs)

   script eq
       on |λ|(a, b)
           a = b
       end |λ|
   end script
   
   groupBy(eq, xs)

end group


-- groupBy :: (a -> a -> Bool) -> [a] -> a on groupBy(f, xs)

   -- Typical usage: groupBy(on(eq, f), xs)
   set mf to mReturn(f)
   
   script enGroup
       on |λ|(a, x)
           if length of (active of a) > 0 then
               set h to item 1 of active of a
           else
               set h to missing value
           end if
           
           if h is not missing value and mf's |λ|(h, x) then
               {active:(active of a) & {x}, sofar:sofar of a}
           else
               {active:{x}, sofar:(sofar of a) & {active of a}}
           end if
       end |λ|
   end script
   
   if length of xs > 0 then
       set dct to foldl(enGroup, {active:{item 1 of xs}, sofar:{}}, rest of xs)
       if length of (active of dct) > 0 then
           sofar of dct & {active of dct}
       else
           sofar of dct
       end if
   else
       {}
   end if

end groupBy


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


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


-- sort :: Ord a => [a] -> [a] on sort(xs)

   ((current application's NSArray's arrayWithArray:xs)'s ¬
       sortedArrayUsingSelector:"compare:") as list

end sort</lang>

Output:
{"1", "5", "6", "b", "g", "s", "t", "z"}

APL

<lang APL>uniques ← (⊂∘⍋⌷⊣)∘(∪(/⍨)(1=(≢⊢))⌸)∘∊</lang>

Output:
      uniques '133252abcdeeffd' 'a6789798st' 'yxcdfgxcyz'
156bgstz

Arturo

<lang rebol>arr: ["133252abcdeeffd" "a6789798st" "yxcdfgxcyz"] str: join arr

print sort select split str 'ch -> 1 = size match str ch</lang>

Output:
1 5 6 b g s t z

AWK

<lang AWK>

  1. syntax: GAWK -f UNIQUE_CHARACTERS.AWK
  2. sorting:
  3. PROCINFO["sorted_in"] is used by GAWK
  4. SORTTYPE is used by Thompson Automation's TAWK

BEGIN {

   PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
   n = split("133252abcdeeffd,a6789798st,yxcdfgxcyz",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)]++
     }
   }
   for (c in arr2) {
     if (arr2[c] == 1) {
       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)

} </lang>

Output:
133252abcdeeffd
a6789798st
yxcdfgxcyz
3 strings, 35 characters, 20 different, 8 unique: 156bgstz

BASIC

<lang basic>10 DEFINT A-Z 20 DIM C(255) 30 READ A$: IF A$="" GOTO 90 40 FOR I=1 TO LEN(A$) 50 A=ASC(MID$(A$,I,1)) 60 C(A)=C(A)+1 70 NEXT I 80 GOTO 30 90 FOR I=1 TO 255 100 IF C(I)=1 THEN A$=A$+CHR$(I) 110 NEXT I 120 PRINT A$ 130 DATA "133252abcdeeffd" 140 DATA "a6789798st" 150 DATA "yxcdfgxcyz" 160 DATA ""</lang>

Output:
156bgstz

BCPL

<lang bcpl>get "libhdr"

let uniques(strings, out) be $( let counts = vec 255

   for i=0 to 255 do counts!i := 0
   until !strings = 0
   $(  let string = !strings
       strings := strings + 1
       for i=1 to string%0 do
           counts!(string%i) := counts!(string%i) + 1
   $)
   out%0 := 0
   for i=0 to 255 if counts!i = 1
   $(  out%0 := out%0 + 1
       out%(out%0) := i
   $)

$)

let start() be $( let strings = vec 3 and out = vec 1+255/BYTESPERWORD

   strings!0 := "133252abcdeeffd"
   strings!1 := "a6789798st"
   strings!2 := "yxcdfgxcyz"
   strings!3 := 0
   
   uniques(strings, out)
   writef("%S*N", out)

$)</lang>

Output:
156bgstz

C

<lang c>#include <stdio.h>

  1. include <string.h>

char *uniques(char *str[], char *buf) {

   static unsigned counts[256];
   unsigned i;
   char *s, *o = buf;
   memset(counts, 0, 256 * sizeof(unsigned));
   
   for (; *str; str++)
       for (s = *str; *s; s++)
           counts[(unsigned) *s]++;
   
   for (i=0; i<256; i++)
       if (counts[i] == 1)
           *o++ = (char) i;
   
   *o = '\0';
   return buf;

}

int main() {

   char buf[256];
   char *strings[] = {
       "133252abcdeeffd",
       "a6789798st",
       "yxcdfgxcyz",
       NULL
   };
   
   printf("%s\n", uniques(strings, buf));
   return 0;

}</lang>

Output:
156bgstz

C++

<lang cpp>#include <iostream>

  1. include <map>

int main() {

   const char* strings[] = {"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"};
   std::map<char, int> count;
   for (const char* str : strings) {
       for (; *str; ++str)
           ++count[*str];
   }
   for (const auto& p : count) {
       if (p.second == 1)
           std::cout << p.first;
   }
   std::cout << '\n';

}</lang>

Output:
156bgstz

Factor

Works with: Factor version 0.99 build 2074

<lang factor>USING: io sequences sets.extras sorting ;

{ "133252abcdeeffd" "a6789798st" "yxcdfgxcyz" } concat non-repeating natural-sort print</lang>

Output:
156bgstz

Go

<lang go>package main

import (

   "fmt"
   "sort"

)

func main() {

   strings := []string{"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"}
   m := make(map[rune]int)
   for _, s := range strings {
       for _, c := range s {
           m[c]++
       }
   }
   var chars []rune
   for k, v := range m {
       if v == 1 {
           chars = append(chars, k)
       }
   }
   sort.Slice(chars, func(i, j int) bool { return chars[i] < chars[j] })
   fmt.Println(string(chars))

}</lang>

Output:
156bgstz

Haskell

<lang haskell>import Data.List (group, sort)

uniques :: [String] -> String uniques ks =

 [c | (c : cs) <- (group . sort . concat) ks, null cs]

main :: IO () main =

 putStrLn $
   uniques
     [ "133252abcdeeffd",
       "a6789798st",
       "yxcdfgxcyz"
     ]</lang>
Output:
156bgstz


Or folding the strings down to a hash of character frequencies: <lang haskell>import qualified Data.Map.Strict as M


UNIQUE CHARACTERS FROM A LIST OF STRINGS -------

uniqueChars :: [String] -> String uniqueChars xs =

 [ k
   | (k, v) <-
       M.assocs $
         foldr
           (flip (foldr (flip (M.insertWith (+)) 1)))
           M.empty
           xs,
     1 == v
 ]

TEST -------------------------

main :: IO () main =

 putStrLn $
   uniqueChars
     [ "133252abcdeeffd",
       "a6789798st",
       "yxcdfgxcyz"
     ]</lang>
Output:
156bgstz

JavaScript

<lang javascript>(() => {

   "use strict";
   // ---------------- UNIQUE CHARACTERS ----------------
   // uniques :: [String] -> [Char]
   const uniques = xs =>
       group(
           xs.flatMap(x => [...x])
           .sort()
       )
       .flatMap(
           x => 1 === x.length ? (
               x
           ) : []
       );
   // ---------------------- TEST -----------------------
   // main :: IO ()
   const main = () =>
       uniques([
           "133252abcdeeffd",
           "a6789798st",
           "yxcdfgxcyz"
       ]);


   // --------------------- GENERIC ---------------------
   // group :: Eq a => [a] -> a
   const group = xs => {
       // A list of lists, each containing only equal elements,
       // such that the concatenation of these lists is xs.
       const go = ys =>
           0 < ys.length ? (() => {
               const
                   h = ys[0],
                   i = ys.findIndex(y => h !== y);
               return i !== -1 ? (
                   [ys.slice(0, i)].concat(go(ys.slice(i)))
               ) : [ys];
           })() : [];
       return go(xs);
   };


   // MAIN ---
   return JSON.stringify(main());

})();</lang>

Output:
["1","5","6","b","g","s","t","z"]


Or, folding the strings (with Array.reduce) down to a hash of character frequencies:

<lang javascript>(() => {

   "use strict";
   // uniqueChars :: [String] -> [Char]
   const uniqueChars = ws =>
       Object.entries(
           ws.reduce(
               (dict, w) => [...w].reduce(
                   (a, c) => Object.assign({}, a, {
                       [c]: 1 + (a[c] || 0)
                   }),
                   dict
               ), {}
           )
       )
       .flatMap(
           ([k, v]) => 1 === v ? (
               [k]
           ) : []
       );
   // ---------------------- TEST -----------------------
   const main = () =>
       uniqueChars([
           "133252abcdeeffd", "a6789798st", "yxcdfgxcyz"
       ]);


   return JSON.stringify(main());

})();</lang>

Output:
["1","5","6","b","s","t","g","z"]

J

<lang J>uniques =: [:/:~~.@;#~1=+/"1@=@;</lang>

Output:
   uniques '133252abcdeeffd';'a6789798st';'yxcdfgxcyz'
156bgstz

jq

Works with: jq

Works with gojq, the Go implementation of jq

The following "bag-of-words" solution is quite efficient as it takes advantage of the fact that jq implements JSON objects as a hash.<lang jq>

  1. bag of words

def bow(stream):

 reduce stream as $word ({}; .[($word|tostring)] += 1);
  1. Input: an array of strings
  2. Output: an array of the characters that appear just once

def in_one_just_once:

 bow( .[] | explode[] | [.] | implode) | with_entries(select(.value==1)) | keys;

</lang> The task <lang jq>["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"] | in_one_just_once</lang>

Output:
["1","5","6","b","g","s","t","z"]

Julia

<lang julia>list = ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]

function is_once_per_all_strings_in(a::Vector{String})

   charlist = collect(prod(a))
   counts = Dict(c => count(x -> c == x, charlist) for c in unique(charlist))
   return sort([p[1] for p in counts if p[2] == 1])

end

println(is_once_per_all_strings_in(list))

</lang>
Output:

['1', '5', '6', 'b', 'g', 's', 't', 'z']

One might think that the method above suffers from too many passes through the text with one pass per count, but with a small text length the dictionary lookup takes more time. Compare times for a single pass version:

<lang julia>function uniquein(a)

   counts = Dict{Char, Int}()
   for c in prod(list)
       counts[c] = get!(counts, c, 0) + 1
   end
   return sort([c for (c, n) in counts if n == 1])

end

println(uniquein(list))

using BenchmarkTools @btime is_once_per_all_strings_in(list) @btime uniquein(list)

</lang>
Output:

['1', '5', '6', 'b', 'g', 's', 't', 'z']

 1.740 μs (28 allocations: 3.08 KiB)
 3.763 μs (50 allocations: 3.25 KiB)

This can be rectified (see Phix entry) if we don't save the counts as we go but just exclude entries with duplicates: <lang julia>function uniquein2(a)

   s = sort(collect(prod(list)))
   l = length(s)
   return [p[2] for p in enumerate(s) if (p[1] == 1 || p[2] != s[p[1] - 1]) && (p[1] == l || p[2] != s[p[1] + 1])]

end

println(uniquein2(list))

@btime uniquein2(list)

</lang>
Output:

['1', '5', '6', 'b', 'g', 's', 't', 'z']

 1.010 μs (14 allocations: 1.05 KiB)

Nim

One solution, but others are possible, for instance concatenating the strings and building the count table from it rather than merging several count tables. And to build the last sequence, we could have used something like sorted(toSeq(charCount.pairs).filterIt(it[1] == 1).mapIt(it[0])), which is a one liner but less readable and less efficient than our solution using “collect”.

<lang Nim>import algorithm, sugar, tables

var charCount: CountTable[char]

for str in ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]:

 charCount.merge str.toCountTable

let uniqueChars = collect(newSeq):

                   for ch, count in charCount.pairs:
                     if count == 1: ch

echo sorted(uniqueChars)</lang>

Output:
@['1', '5', '6', 'b', 'g', 's', 't', 'z']

Perl

Translation of: Raku

<lang perl># 20210506 Perl programming solution

use strict; use warnings; use utf8; use Unicode::Collate 'sort';

my %seen; binmode(STDOUT, ':encoding(utf8)'); map { s/(\X)/$seen{$1}++/egr }

  "133252abcdeeffd", "a6789798st", "yxcdfgxcyz", "AАΑSäaoö٥🤔👨‍👩‍👧‍👧";

my $uca = Unicode::Collate->new(); print $uca->sort ( grep { $seen{$_} == 1 } keys %seen )</lang>

Output:
👨‍👩‍👧‍👧🤔15٥6AäbgoösStzΑА

Phix

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 = {"133252abcdeeffd","a6789798st","yxcdfgxcyz"},
         res = filter(sort(join(set,"")),once)
printf(1,"found %d unique characters: %s\n",{length(res),res})
Output:
found 8 unique characters: 156bgstz

PicoLisp

<lang PicoLisp>(de uni (Lst

  (let R NIL
     (mapc
        '((L)
           (mapc
              '((L) (accu 'R L 1)) L ) )
        (mapcar chop Lst) )
     (mapcar
        car
        (by
           car
           sort
           (filter '((L) (=1 (cdr L))) R) ) ) ) )

(println

  (uni
     (quote
        "133252abcdeeffd"
        "a6789798st"
        "yxcdfgxcyz" ) ) )</lang>
Output:
("1" "5" "6" "b" "g" "s" "t" "z")

PL/M

<lang pli>100H: BDOS: PROCEDURE (FN, ARG); DECLARE FN BYTE, ARG ADDRESS; GO TO 5; END BDOS; EXIT: PROCEDURE; CALL BDOS(0,0); END EXIT; PRINT: PROCEDURE (S); DECLARE S ADDRESS; CALL BDOS(9,S); END PRINT;

/* FIND SORTED UNIQUE CHARACTERS IN ARRAY OF STRINGS */ UNIQUES: PROCEDURE (STRINGS, OUT);

   DECLARE (STRINGS, OUT) ADDRESS;
   DECLARE STRING BASED STRINGS ADDRESS; 
   DECLARE IPTR ADDRESS;
   DECLARE ICHAR BASED IPTR BYTE;
   DECLARE OCHAR BASED OUT BYTE;
   DECLARE COUNT (256) BYTE;
   DECLARE I ADDRESS;
   
   DO I=0 TO 255; COUNT(I)=0; END;
   I = 0;
   DO WHILE STRING(I) <> 0;
       IPTR = STRING(I);
       DO WHILE ICHAR <> '$';
           COUNT(ICHAR) = COUNT(ICHAR) + 1;
           IPTR = IPTR + 1;
       END;
       I = I + 1;
   END;
   
   DO I=0 TO 255;
       IF COUNT(I) = 1 THEN DO;
           OCHAR = I;
           OUT = OUT + 1;
       END;
   END;
   
   OCHAR = '$';

END UNIQUES;

/* INPUT ARRAY */ /* USING UPPERCASE LETTERS BECAUSE PLM-80 DOES NOT SUPPORT LOWERCASE */ DECLARE STRINGS (4) ADDRESS; STRINGS(0) = .'133252ABCDEEFFD$'; STRINGS(1) = .'A6789798ST$'; STRINGS(2) = .'YXCDFGXCYZ$'; STRINGS(3) = 0;

DECLARE BUFFER (255) BYTE; CALL UNIQUES(.STRINGS, .BUFFER); CALL PRINT(.BUFFER); CALL EXIT; EOF</lang>

Output:
156BGSTZ

Python

<lang python>Unique characters

from itertools import chain, groupby


  1. uniques :: [String] -> [Char]

def uniques(xs):

   Characters which occur only once
      across the given list of strings.
   
   return [
       h for h, (_, *tail) in
       groupby(sorted(chain(*xs)))
       if not tail
   ]


  1. ------------------------- TEST -------------------------
  2. main :: IO ()

def main():

   Characters occurring only once
      across a list of 3 given strings.
   
   print(
       uniques([
           "133252abcdeeffd",
           "a6789798st",
           "yxcdfgxcyz"
       ])
   )


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
['1', '5', '6', 'b', 'g', 's', 't', 'z']

Raku

One has to wonder where the digits 0 through 9 come in the alphabet... 🤔 For that matter, What alphabet should they be in order of? Most of these entries seem to presuppose ASCII order but that isn't specified anywhere. What to do with characters outside of ASCII (or Latin-1)? Unicode ordinal order? Or maybe DUCET Unicode collation order? It's all very vague.

<lang perl6>my @list = <133252abcdeeffd a6789798st yxcdfgxcyz>;

for @list, (@list, 'AАΑSäaoö٥🤔👨‍👩‍👧‍👧') {

   say "$_\nSemi-bogus \"Unicode natural sort\" order: ",
   .map( *.comb ).Bag.grep( *.value == 1 )».key.sort( { .unival, .NFKD[0], .fc } ).join,
   "\n        (DUCET) Unicode collation order: ",
   .map( *.comb ).Bag.grep( *.value == 1 )».key.collate.join, "\n";

}</lang>

Output:
133252abcdeeffd a6789798st yxcdfgxcyz
Semi-bogus "Unicode natural sort" order: 156bgstz
        (DUCET) Unicode collation order: 156bgstz

133252abcdeeffd a6789798st yxcdfgxcyz AАΑSäaoö٥🤔👨‍👩‍👧‍👧
Semi-bogus "Unicode natural sort" order: 15٥6ASäbgoöstzΑА👨‍👩‍👧‍👧🤔
        (DUCET) Unicode collation order: 👨‍👩‍👧‍👧🤔ä15٥6AbögosStzΑА

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. <lang rexx>/*REXX pgm finds and shows characters that are unique to only one string and once only.*/ parse arg $ /*obtain optional arguments from the CL*/ if $= | $="," then $= '133252abcdeeffd' "a6789798st" 'yxcdfgxcyz' /*use defaults.*/ if $= then do; say "***error*** no lists were specified."; exit 13; end @= /*will be a list of all unique chars. */

   do j=0  for 256;     x= d2c(j)               /*process all the possible characters. */
                        if x==' '  then iterate /*ignore blanks which are a delimiter. */
   _= pos(x, $);        if _==0    then iterate /*character not found,  then skip it.  */
   _= pos(x, $, _+1);   if _ >0    then iterate /*Character is a duplicate?  Skip it.  */
   @= @ x
   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. */</lang>

output   when using the default inputs:
unique characters are:   1 5 6 b g s t z

Found  8  unique characters.

Ring

<lang ring> see "working..." + nl see "Unique characters are:" + nl row = 0 str = "" cList = [] uniqueChars = ["133252abcdeeffd", "a6789798st","yxcdfgxcyz"] for n = 1 to len(uniqueChars)

   str = str + uniqueChars[n]

next for n = 1 to len(str)

   ind = count(str,str[n])
   if ind = 1
      row = row + 1
      add(cList,str[n])
   ok

next cList = sort(cList) for n = 1 to len(cList)

   see "" + cList[n] + " "

next see nl

see "Found " + row + " unique characters" + 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

</lang>

Output:
working...
Unique characters are:
1 5 6 b g s t z 
Found 8 unique characters
done...

Wren

Library: Wren-seq
Library: Wren-sort

<lang ecmascript>import "/seq" for Lst import "/sort" for Sort

var strings = ["133252abcdeeffd", "a6789798st","yxcdfgxcyz"] var totalChars = strings.reduce { |acc, str| acc + str }.toList var uniqueChars = Lst.individuals(totalChars).where { |l| l[1] == 1 }.map { |l| l[0] }.toList Sort.insertion(uniqueChars) System.print("Found %(uniqueChars.count) unique character(s), namely:") System.print(uniqueChars.join(" "))</lang>

Output:
Found 8 unique character(s), namely:
1 5 6 b g s t z

XPL0

<lang XPL0>int List, I, N, C; char Tbl(128), Str; string 0; [List:= ["133252abcdeeffd", "a6789798st","yxcdfgxcyz"]; for I:= 0 to 127 do Tbl(I):= 0; for N:= 0 to 2 do

       [Str:= List(N);
       I:= 0;
       loop    [C:= Str(I);
               if C = 0 then quit;
               I:= I+1;
               Tbl(C):= Tbl(C)+1;
               ];
       ];

for I:= 0 to 127 do

       if Tbl(I) = 1 then ChOut(0, I);

]</lang>

Output:
156bgstz