Unique characters: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Python}}: Added a variant which reduces the strings down to a hash of character frequencies)
(Add two StandardML versions)
 
(38 intermediate revisions by 22 users not shown)
Line 13: Line 13:
{{Template:Strings}}
{{Template:Strings}}
<br><br>
<br><br>

=={{header|11l}}==
<syntaxhighlight lang="11l">DefaultDict[Char, Int] d
L(s) [‘133252abcdeeffd’, ‘a6789798st’, ‘yxcdfgxcyz’]
L(c) s
d[c]++

L(k) sorted(d.keys())
I d[k] == 1
print(k, end' ‘’)</syntaxhighlight>

{{out}}
<pre>
156bgstz
</pre>


=={{header|8080 Assembly}}==
=={{header|8080 Assembly}}==
<lang 8080asm>puts: equ 9 ; CP/M print syscall
<syntaxhighlight lang="8080asm">puts: equ 9 ; CP/M print syscall
TERM: equ '$' ; CP/M string terminator
TERM: equ '$' ; CP/M string terminator
org 100h
org 100h
Line 71: Line 86:
;;; Memory
;;; Memory
upage: equ ($/256)+1 ; Workspace for 'unique'
upage: equ ($/256)+1 ; Workspace for 'unique'
outbuf: equ (upage+1)*256 ; Output </lang>
outbuf: equ (upage+1)*256 ; Output </syntaxhighlight>
{{out}}
{{out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>



=={{header|8086 Assembly}}==
=={{header|8086 Assembly}}==
<lang asm> cpu 8086
<syntaxhighlight lang="asm"> cpu 8086
org 100h
org 100h
puts: equ 9 ; MS-DOS syscall to print a string
puts: equ 9 ; MS-DOS syscall to print a string
Line 127: Line 141:
section .bss
section .bss
uniqws: resb 256
uniqws: resb 256
outbuf: resb 256</lang>
outbuf: resb 256</syntaxhighlight>
{{out}}
{{out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>

=={{header|Action!}}==
<syntaxhighlight lang="action!">DEFINE MAX="128"
CHAR ARRAY counts(MAX)

BYTE FUNC GetCount(CHAR ARRAY s CHAR c)
BYTE count,i

count=0
FOR i=1 TO s(0)
DO
IF s(i)=c THEN
count==+1
FI
OD
RETURN (count)

PROC UpdateCounts(CHAR ARRAY s)
BYTE i,c

FOR i=1 TO s(0)
DO
c=s(i)
counts(c)==+GetCount(s,c)
OD
RETURN

PROC Main()
DEFINE PTR="CARD"
DEFINE CNT="3"
PTR ARRAY l(CNT)
INT i

l(0)="133252abcdeeffd"
l(1)="a6789798st"
l(2)="yxcdfgxcyz"

SetBlock(counts,MAX,0)
FOR i=0 TO CNT-1
DO
UpdateCounts(l(i))
OD
FOR i=0 TO MAX-1
DO
IF counts(i)=1 THEN
Put(i) Put(32)
FI
OD
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Unique_characters.png Screenshot from Atari 8-bit computer]
<pre>
1 5 6 b g s t z
</pre>

=={{header|Ada}}==
<syntaxhighlight lang="ada">with Ada.Text_Io;

procedure Unique_Characters is

List : array (Character) of Natural := (others => 0);

procedure Count (Item : String) is
begin
for C of Item loop
List (C) := List (C) + 1;
end loop;
end Count;

procedure Put_Only_Once is
use Ada.Text_Io;
begin
for C in List'Range loop
if List (C) = 1 then
Put (C);
Put (' ');
end if;
end loop;
New_Line;
end Put_Only_Once;

begin
Count ("133252abcdeeffd");
Count ("a6789798st");
Count ("yxcdfgxcyz");
Put_Only_Once;
end Unique_Characters;</syntaxhighlight>
{{out}}
<pre>1 5 6 b g s t z</pre>

=={{header|ALGOL 68}}==
Case sensitive. This assumes a small character set (e.g. ASCII where max abs char is 255). Would probably need some work if CHAR is Unicode.
<syntaxhighlight lang="algol68">BEGIN # find the characters that occur once only in a list of stings #
# returns the characters that occur only once in the elements of s #
OP UNIQUE = ( []STRING s )STRING:
BEGIN
[ 0 : max abs char ]INT counts; # counts of used characters #
FOR i FROM LWB counts TO UPB counts DO counts[ 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 s[ i ] TO UPB s[ i ] DO
counts[ ABS s[ i ][ j ] ] +:= 1
OD
OD;
# construct a string of the characters that occur only once #
STRING result := "";
FOR i FROM LWB counts TO UPB counts DO
IF counts[ i ] = 1 THEN result +:= REPR i FI
OD;
result
END; # UNIQUE #
# task test case #
print( ( UNIQUE []STRING( "133252abcdeeffd", "a6789798st", "yxcdfgxcyz" ), newline ) )
END</syntaxhighlight>
{{out}}
<pre>
156bgstz
</pre>


=={{header|AppleScript}}==
=={{header|AppleScript}}==
Line 135: Line 267:
The filtering here is case sensitive, the sorting dependent on locale.
The filtering here is case sensitive, the sorting dependent on locale.


<lang applescript>on uniqueCharacters(listOfStrings)
<syntaxhighlight lang="applescript">on uniqueCharacters(listOfStrings)
set astid to AppleScript's text item delimiters
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
set AppleScript's text item delimiters to ""
Line 147: Line 279:
return (mutableSet's sortedArrayUsingDescriptors:({sortDescriptor})) as list
return (mutableSet's sortedArrayUsingDescriptors:({sortDescriptor})) as list
end uniqueCharacters</lang>
end uniqueCharacters</syntaxhighlight>


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


===Core language only===
===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.
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
<syntaxhighlight 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>
use sorter : script "Heap Sort" -- <https://www.rosettacode.org/wiki/Sorting_algorithms/Heapsort#AppleScript>


Line 189: Line 321:
considering case
considering case
return uniqueCharacters({"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"})
return uniqueCharacters({"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"})
end considering</lang>
end considering</syntaxhighlight>




Line 196: Line 328:
Composing a solution from existing generic primitives, for speed of drafting and refactoring, and for high levels of code reuse.
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"
<syntaxhighlight lang="applescript">use framework "Foundation"




Line 349: Line 481:
((current application's NSArray's arrayWithArray:xs)'s ¬
((current application's NSArray's arrayWithArray:xs)'s ¬
sortedArrayUsingSelector:"compare:") as list
sortedArrayUsingSelector:"compare:") as list
end sort</lang>
end sort</syntaxhighlight>
{{Out}}
{{Out}}
<pre>{"1", "5", "6", "b", "g", "s", "t", "z"}</pre>
<pre>{"1", "5", "6", "b", "g", "s", "t", "z"}</pre>


=={{header|APL}}==
=={{header|APL}}==
<lang APL>uniques ← (⊂∘⍋⌷⊣)∘(∪(/⍨)(1=(≢⊢))⌸)∘∊</lang>
<syntaxhighlight lang="apl">uniques ← (⊂∘⍋⌷⊣)∘(∪(/⍨)(1=(≢⊢))⌸)∘∊</syntaxhighlight>
{{out}}
{{out}}
<pre> uniques '133252abcdeeffd' 'a6789798st' 'yxcdfgxcyz'
<pre> uniques '133252abcdeeffd' 'a6789798st' 'yxcdfgxcyz'
Line 361: Line 493:
=={{header|Arturo}}==
=={{header|Arturo}}==


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


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


{{out}}
{{out}}
Line 371: Line 503:


=={{header|AWK}}==
=={{header|AWK}}==
<syntaxhighlight lang="awk">
<lang AWK>
# syntax: GAWK -f UNIQUE_CHARACTERS.AWK
# syntax: GAWK -f UNIQUE_CHARACTERS.AWK
#
#
Line 397: Line 529:
exit(0)
exit(0)
}
}
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 407: Line 539:


=={{header|BASIC}}==
=={{header|BASIC}}==
<lang basic>10 DEFINT A-Z
<syntaxhighlight lang="basic">10 DEFINT A-Z
20 DIM C(255)
20 DIM C(255)
30 READ A$: IF A$="" GOTO 90
30 READ A$: IF A$="" GOTO 90
Line 422: Line 554:
140 DATA "a6789798st"
140 DATA "a6789798st"
150 DATA "yxcdfgxcyz"
150 DATA "yxcdfgxcyz"
160 DATA ""</lang>
160 DATA ""</syntaxhighlight>
{{out}}
{{out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>


=={{header|BCPL}}==
=={{header|BCPL}}==
<lang bcpl>get "libhdr"
<syntaxhighlight lang="bcpl">get "libhdr"


let uniques(strings, out) be
let uniques(strings, out) be
Line 455: Line 587:
uniques(strings, out)
uniques(strings, out)
writef("%S*N", out)
writef("%S*N", out)
$)</lang>
$)</syntaxhighlight>
{{out}}
{{out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>

=={{header|BQN}}==
<syntaxhighlight lang="bqn">Uniq ← (⍷/˜1=/⁼∘⊐)∧∘∾</syntaxhighlight>

{{out}}
<pre>
Uniq "133252abcdeeffd"‿"a6789798st"‿"yxcdfgxcyz"
"156bgstz"
</pre>


=={{header|C}}==
=={{header|C}}==
<lang c>#include <stdio.h>
<syntaxhighlight lang="c">#include <stdio.h>
#include <string.h>
#include <string.h>


Line 492: Line 633:
printf("%s\n", uniques(strings, buf));
printf("%s\n", uniques(strings, buf));
return 0;
return 0;
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>


=={{header|C++}}==
=={{header|C++}}==
<lang cpp>#include <iostream>
<syntaxhighlight lang="cpp">#include <iostream>
#include <map>
#include <map>


Line 512: Line 653:
}
}
std::cout << '\n';
std::cout << '\n';
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 518: Line 659:
156bgstz
156bgstz
</pre>
</pre>

=={{header|Delphi}}==
{{works with|Delphi|6.0}}
{{libheader|SysUtils,StdCtrls}}


<syntaxhighlight lang="Delphi">

var SA: array [0..2] of string = ('133252abcdeeffd', 'a6789798st', 'yxcdfgxcyz');

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;

procedure ShowUniqueChars(Memo: TMemo);
var I: integer;
var S: string;
begin
{Concatonate all strings}
S:='';
for I:=0 to High(SA) do S:=S+SA[I];
{Get all characters that appear once}
S:=CharsAppearingOnce(S);
Memo.Lines.Add(S);
end;


</syntaxhighlight>
{{out}}
<pre>
156bgstz

Elapsed Time: 0.959 ms.

</pre>



=={{header|Factor}}==
=={{header|Factor}}==
{{works with|Factor|0.99 build 2074}}
{{works with|Factor|0.99 build 2074}}
<lang factor>USING: io sequences sets.extras sorting ;
<syntaxhighlight lang="factor">USING: io sequences sets.extras sorting ;


{ "133252abcdeeffd" "a6789798st" "yxcdfgxcyz" }
{ "133252abcdeeffd" "a6789798st" "yxcdfgxcyz" }
concat non-repeating natural-sort print</lang>
concat non-repeating natural-sort print</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
156bgstz
156bgstz
</pre>
</pre>


=={{header|FreeBASIC}}==
<syntaxhighlight lang="freebasic">Dim As Integer c(255), i, a
Dim As String s
Do
Read s
For i = 1 To Len(s)
a = Asc(Mid(s,i,1))
c(a) += 1
Next i
Loop Until s = ""

For i = 1 To 255
If c(i) = 1 Then s &= Chr(i)
Next i
Print s

Data "133252abcdeeffd", "a6789798st", "yxcdfgxcyz", ""
Sleep</syntaxhighlight>
{{out}}
<pre>156bgstz</pre>

=={{header|FutureBasic}}==
<syntaxhighlight lang="futurebasic">window 1, @"Unique characters"

void local fn DoIt
CountedSetRef set = fn CountedSetWithCapacity(0)
CFArrayRef array = @[@"133252abcdeeffd",@"a6789798st",@"yxcdfgxcyz"]
CFStringRef string, chr
long index
CFMutableArrayRef mutArray = fn MutableArrayWithCapacity(0)
for string in array
for index = 0 to len(string) - 1
CountedSetAddObject( set, mid(string,index,1) )
next
next
for chr in set
if ( fn CountedSetCountForObject( set, chr ) == 1 )
MutableArrayAddObject( mutArray, chr )
end if
next
MutableArraySortUsingSelector( mutArray, @"compare:" )
print fn ArrayComponentsJoinedByString( mutArray, @"" )
end fn

fn DoIt

HandleEvents</syntaxhighlight>
{{out}}
<pre>156bgstz</pre>


=={{header|Go}}==
=={{header|Go}}==
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 554: Line 808:
sort.Slice(chars, func(i, j int) bool { return chars[i] < chars[j] })
sort.Slice(chars, func(i, j int) bool { return chars[i] < chars[j] })
fmt.Println(string(chars))
fmt.Println(string(chars))
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 562: Line 816:


=={{header|Haskell}}==
=={{header|Haskell}}==
<lang haskell>import Data.List (group, sort)
<syntaxhighlight lang="haskell">import Data.List (group, sort)


uniques :: [String] -> String
uniques :: [String] -> String
Line 575: Line 829:
"a6789798st",
"a6789798st",
"yxcdfgxcyz"
"yxcdfgxcyz"
]</lang>
]</syntaxhighlight>
{{Out}}
{{Out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>
Line 581: Line 835:


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


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


uniqueChars :: [String] -> String
uniqueChars :: [String] -> String
uniqueChars xs =
uniqueChars =
M.keys . M.filter (1 ==)
[ k
. foldr (M.unionWith (+) . charCounts) M.empty
| (k, v) <-

M.assocs $
charCounts :: String -> M.Map Char Int
foldr
charCounts =
(flip (foldr (flip (M.insertWith (+)) 1)))
foldr
M.empty
(flip (M.insertWith (+)) 1)
xs,
1 == v
M.empty
]


--------------------------- TEST -------------------------
--------------------------- TEST -------------------------
Line 605: Line 858:
"a6789798st",
"a6789798st",
"yxcdfgxcyz"
"yxcdfgxcyz"
]</lang>
]</syntaxhighlight>
{{Out}}
{{Out}}
<pre>156bgstz</pre>
<pre>156bgstz</pre>


=={{header|JavaScript}}==
=={{header|JavaScript}}==
<lang javascript>(() => {
<syntaxhighlight lang="javascript">(() => {
"use strict";
"use strict";


Line 660: Line 913:
// MAIN ---
// MAIN ---
return JSON.stringify(main());
return JSON.stringify(main());
})();</lang>
})();</syntaxhighlight>
{{Out}}
{{Out}}
<pre>["1","5","6","b","g","s","t","z"]</pre>
<pre>["1","5","6","b","g","s","t","z"]</pre>
Line 667: Line 920:
Or, folding the strings (with Array.reduce) down to a hash of character frequencies:
Or, folding the strings (with Array.reduce) down to a hash of character frequencies:


<lang javascript>(() => {
<syntaxhighlight lang="javascript">(() => {
"use strict";
"use strict";


Line 696: Line 949:


return JSON.stringify(main());
return JSON.stringify(main());
})();</lang>
})();</syntaxhighlight>
{{Out}}
{{Out}}
<pre>["1","5","6","b","s","t","g","z"]</pre>
<pre>["1","5","6","b","s","t","g","z"]</pre>


=={{header|J}}==
=={{header|J}}==
The simple approach here is to merge the argument strings and find characters which occur exactly once in that intermediate result:
<lang J>uniques =: [:/:~~.@;#~1=+/"1@=@;</lang>
<syntaxhighlight lang="j">uniques=: ~.#~1=#/.~</syntaxhighlight>
In other words, <code>~.</code> finds the distinct characters, <code>#/.~</code> finds the corresponding counts of those characters, so <code>1=#/.~</code> is true for the characters which occur exactly once, and <code>#~</code> filters the distinct characters based on those truth values.
{{out}}
{{out}}
<pre> uniques '133252abcdeeffd';'a6789798st';'yxcdfgxcyz'
<pre> uniques;'133252abcdeeffd';'a6789798st';'yxcdfgxcyz'
156bgstz</pre>
156bgstz</pre>

Here, <code>;</code> as a separator between quoted strings builds a list of the strings, and <code>;</code> as a prefix of that list merges the contents of the strings of that list into a single string. We could just as easily have formed a single string, but that's not what the task asked for. (Since <code>uniques</code> is a verb (aka a "function"), it's not a list element in this context.)


=={{header|jq}}==
=={{header|jq}}==
Line 710: Line 967:
'''Works with gojq, the Go implementation of 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>
The following "bag-of-words" solution is quite efficient as it takes advantage of the fact that jq implements JSON objects as a hash.<syntaxhighlight lang="jq">
# bag of words
# bag of words
def bow(stream):
def bow(stream):
Line 719: Line 976:
def in_one_just_once:
def in_one_just_once:
bow( .[] | explode[] | [.] | implode) | with_entries(select(.value==1)) | keys;
bow( .[] | explode[] | [.] | implode) | with_entries(select(.value==1)) | keys;
</syntaxhighlight>
</lang>
'''The task'''
'''The task'''
<lang jq>["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
<syntaxhighlight lang="jq">["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
| in_one_just_once</lang>
| in_one_just_once</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 729: Line 986:


=={{header|Julia}}==
=={{header|Julia}}==
<lang julia>list = ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
<syntaxhighlight lang="julia">list = ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]


function is_once_per_all_strings_in(a::Vector{String})
function is_once_per_all_strings_in(a::Vector{String})
Line 738: Line 995:


println(is_once_per_all_strings_in(list))
println(is_once_per_all_strings_in(list))
</lang>{{out}}<pre>
</syntaxhighlight>{{out}}<pre>
['1', '5', '6', 'b', 'g', 's', 't', 'z']
['1', '5', '6', 'b', 'g', 's', 't', 'z']
</pre>
</pre>
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:
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)
<syntaxhighlight lang="julia">function uniquein(a)
counts = Dict{Char, Int}()
counts = Dict{Char, Int}()
for c in prod(list)
for c in prod(list)
Line 756: Line 1,013:
@btime is_once_per_all_strings_in(list)
@btime is_once_per_all_strings_in(list)
@btime uniquein(list)
@btime uniquein(list)
</lang>{{out}}<pre>
</syntaxhighlight>{{out}}<pre>
['1', '5', '6', 'b', 'g', 's', 't', 'z']
['1', '5', '6', 'b', 'g', 's', 't', 'z']
1.740 μs (28 allocations: 3.08 KiB)
1.740 μs (28 allocations: 3.08 KiB)
Line 763: Line 1,020:


This can be rectified (see Phix entry) if we don't save the counts as we go but just exclude entries with duplicates:
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)
<syntaxhighlight lang="julia">function uniquein2(a)
s = sort(collect(prod(list)))
s = sort(collect(prod(list)))
l = length(s)
l = length(s)
Line 772: Line 1,029:


@btime uniquein2(list)
@btime uniquein2(list)
</lang>{{out}}<pre>
</syntaxhighlight>{{out}}<pre>
['1', '5', '6', 'b', 'g', 's', 't', 'z']
['1', '5', '6', 'b', 'g', 's', 't', 'z']
1.010 μs (14 allocations: 1.05 KiB)
1.010 μs (14 allocations: 1.05 KiB)
</pre>
</pre>


=={{header|Lua}}==
<syntaxhighlight lang="lua">local strings = {"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"}
unpack = unpack or table.unpack -- compatibility for all Lua versions

local map = {}
for i, str in ipairs (strings) do
for i=1, string.len(str) do
local char = string.sub(str,i,i)
if map[char] == nil then
map[char] = true
else
map[char] = false
end
end
end

local list = {}
for char, bool in pairs (map) do
if bool then
table.insert (list, char)
end
end
table.sort (list)
print (unpack (list))</syntaxhighlight>
{{out}}<pre>1 5 6 b g s t z</pre>

=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">Select[Tally[Sort[Characters[StringJoin[{"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"}]]]], Last /* EqualTo[1]][[All, 1]]</syntaxhighlight>
{{out}}
<pre>{"1", "5", "6", "b", "g", "s", "t", "z"}</pre>


=={{header|Nim}}==
=={{header|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 <code>sorted(toSeq(charCount.pairs).filterIt(it[1] == 1).mapIt(it[0]))</code>, which is a one liner but less readable and less efficient than our solution using “collect”.
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 <code>sorted(toSeq(charCount.pairs).filterIt(it[1] == 1).mapIt(it[0]))</code>, which is a one liner but less readable and less efficient than our solution using “collect”.


<lang Nim>import algorithm, sugar, tables
<syntaxhighlight lang="nim">import algorithm, sugar, tables


var charCount: CountTable[char]
var charCount: CountTable[char]
Line 791: Line 1,082:
if count == 1: ch
if count == 1: ch


echo sorted(uniqueChars)</lang>
echo sorted(uniqueChars)</syntaxhighlight>


{{out}}
{{out}}
<pre>@['1', '5', '6', 'b', 'g', 's', 't', 'z']</pre>
<pre>@['1', '5', '6', 'b', 'g', 's', 't', 'z']</pre>

=={{header|Pascal}}==
{{works with|Extended Pascal}}
<syntaxhighlight lang="pascal">program uniqueCharacters(output);

type
{ `integer…` prefix to facilitate sorting in documentation tools }
integerNonNegative = 0..maxInt;
{ all variables of this data type will be initialized to `1` }
integerPositive = 1..maxInt value 1;
charFrequency = array[char] of integerNonNegative;
{ This “discriminates” the `string` _schema_ data type. }
line = string(80);
{ A schema data type definition looks sort of like a function: }
lines(length: integerPositive) = array[1..length] of line;

{ `protected` in Extended Pascal means, you can’t modify this parameter. }
function createStatistics(protected sample: lines): charFrequency;
var
i, n: integerPositive;
{ The `value …` clause will initialize this variable to the given value. }
statistics: charFrequency value [chr(0)..maxChar: 0];
begin
{ You can inspect the discriminant of schema data type just like this: }
for n := 1 to sample.length do
begin
{ The length of a `string(…)` though needs to be queried: }
for i := 1 to length(sample[n]) do
begin
statistics[sample[n, i]] := statistics[sample[n, i]] + 1
end
end;
{ There needs to be exactly one assignment to the result variable: }
createStatistics := statistics
end;

{ === MAIN ============================================================= }
var
c: char;
sample: lines(3) value [1: '133252abcdeeffd';
2: 'a6789798st'; 3: 'yxcdfgxcyz'];
statistics: charFrequency;
begin
statistics := createStatistics(sample);
for c := chr(0) to maxChar do
begin
{
The colon (`:`) specifies the display width of the parameter.
While for `integer` and `real` values it means the _minimum_ width,
for `string` and `char` values it is the _exact_ width.
}
write(c:ord(statistics[c] = 1))
end;
writeLn
end.</syntaxhighlight>
{{out}}
<pre>156bgstz</pre>


=={{header|Perl}}==
=={{header|Perl}}==
{{trans|Raku}}
{{trans|Raku}}
<lang perl># 20210506 Perl programming solution
<syntaxhighlight lang="perl"># 20210506 Perl programming solution


use strict;
use strict;
Line 810: Line 1,163:
"133252abcdeeffd", "a6789798st", "yxcdfgxcyz", "AАΑSäaoö٥🤔👨‍👩‍👧‍👧";
"133252abcdeeffd", "a6789798st", "yxcdfgxcyz", "AАΑSäaoö٥🤔👨‍👩‍👧‍👧";
my $uca = Unicode::Collate->new();
my $uca = Unicode::Collate->new();
print $uca->sort ( grep { $seen{$_} == 1 } keys %seen )</lang>
print $uca->sort ( grep { $seen{$_} == 1 } keys %seen )</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 817: Line 1,170:


=={{header|Phix}}==
=={{header|Phix}}==
<!--<lang Phix>(phixonline)-->
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">function</span> <span style="color: #000000;">once</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">once</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">l</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">l</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
Line 827: Line 1,180:
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sort</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">set</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)),</span><span style="color: #000000;">once</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sort</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">set</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)),</span><span style="color: #000000;">once</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"found %d unique characters: %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">),</span><span style="color: #000000;">res</span><span style="color: #0000FF;">})</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"found %d unique characters: %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">),</span><span style="color: #000000;">res</span><span style="color: #0000FF;">})</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 834: Line 1,187:


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp>(de uni (Lst
<syntaxhighlight lang="picolisp">(de uni (Lst
(let R NIL
(let R NIL
(mapc
(mapc
Line 852: Line 1,205:
"133252abcdeeffd"
"133252abcdeeffd"
"a6789798st"
"a6789798st"
"yxcdfgxcyz" ) ) )</lang>
"yxcdfgxcyz" ) ) )</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 859: Line 1,212:


=={{header|PL/M}}==
=={{header|PL/M}}==
<lang pli>100H:
<syntaxhighlight lang="pli">100H:
BDOS: PROCEDURE (FN, ARG); DECLARE FN BYTE, ARG ADDRESS; GO TO 5; END BDOS;
BDOS: PROCEDURE (FN, ARG); DECLARE FN BYTE, ARG ADDRESS; GO TO 5; END BDOS;
EXIT: PROCEDURE; CALL BDOS(0,0); END EXIT;
EXIT: PROCEDURE; CALL BDOS(0,0); END EXIT;
Line 907: Line 1,260:
CALL PRINT(.BUFFER);
CALL PRINT(.BUFFER);
CALL EXIT;
CALL EXIT;
EOF</lang>
EOF</syntaxhighlight>
{{out}}
{{out}}
<pre>156BGSTZ</pre>
<pre>156BGSTZ</pre>


=={{header|Python}}==
=={{header|Python}}==
<lang python>'''Unique characters'''
<syntaxhighlight lang="python">'''Unique characters'''

from itertools import chain, groupby


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


# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''Characters occurring only once
across a list of 3 given strings.
'''
print(
uniques([
"133252abcdeeffd",
"a6789798st",
"yxcdfgxcyz"
])
)


# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
<pre>['1', '5', '6', 'b', 'g', 's', 't', 'z']</pre>

Or reducing the given strings down to a hash of character frequencies:
<syntaxhighlight lang="python">'''Unique characters'''


from functools import reduce
from functools import reduce
Line 922: Line 1,314:
across the given list of strings.
across the given list of strings.
'''
'''
def addedWord(dct, w):
return reduce(updatedCharCount, w, dct)

def updatedCharCount(a, c):
return dict(
a, **{
c: 1 + a[c] if c in a else 1
}
)

return sorted([
return sorted([
k for (k, v) in reduce(
k for k, v in reduce(addedWord, ws, {}).items()
lambda dct, w: reduce(
lambda a, c: dict(a, **{
c: 1 + a[c] if c in a else 1
}),
w,
dct
),
ws,
{}
).items()
if 1 == v
if 1 == v
])
])
Line 953: Line 1,345:
# MAIN ---
# MAIN ---
if __name__ == '__main__':
if __name__ == '__main__':
main()</lang>
main()</syntaxhighlight>
{{Out}}
{{Out}}
<pre>['1', '5', '6', 'b', 'g', 's', 't', 'z']</pre>
<pre>['1', '5', '6', 'b', 'g', 's', 't', 'z']</pre>

=={{header|Quackery}}==

<syntaxhighlight lang="Quackery"> [ [] swap
witheach join
[] 0 128 of
rot witheach
[ 2dup peek
1+ unrot poke ]
witheach
[ 1 = if
[ i^ join ] ] ] is task ( [ --> $ )

[]
$ "133252abcdeeffd" nested join
$ "a6789798st" nested join
$ "yxcdfgxcyz" nested join

task echo$</syntaxhighlight>

{{out}}

<pre>156bgstz</pre>


=={{header|Raku}}==
=={{header|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.
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>;
<syntaxhighlight lang="raku" line>my @list = <133252abcdeeffd a6789798st yxcdfgxcyz>;


for @list, (@list, 'AАΑSäaoö٥🤔👨‍👩‍👧‍👧') {
for @list, (@list, 'AАΑSäaoö٥🤔👨‍👩‍👧‍👧') {
Line 967: Line 1,382:
"\n (DUCET) Unicode collation order: ",
"\n (DUCET) Unicode collation order: ",
.map( *.comb ).Bag.grep( *.value == 1 )».key.collate.join, "\n";
.map( *.comb ).Bag.grep( *.value == 1 )».key.collate.join, "\n";
}</lang>
}</syntaxhighlight>
{{out}}
{{out}}
<pre>133252abcdeeffd a6789798st yxcdfgxcyz
<pre>133252abcdeeffd a6789798st yxcdfgxcyz
Line 987: Line 1,402:


On an &nbsp;'''EBCDIC'''&nbsp; machine, &nbsp; the lowercase letters and the uppercase letters &nbsp; aren't &nbsp; contiguous.
On an &nbsp;'''EBCDIC'''&nbsp; machine, &nbsp; the lowercase letters and the uppercase letters &nbsp; aren't &nbsp; contiguous.
<lang rexx>/*REXX pgm finds and shows characters that are unique to only one string and once only.*/
<syntaxhighlight 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*/
parse arg $ /*obtain optional arguments from the CL*/
if $='' | $="," then $= '133252abcdeeffd' "a6789798st" 'yxcdfgxcyz' /*use defaults.*/
if $='' | $="," then $= '133252abcdeeffd' "a6789798st" 'yxcdfgxcyz' /*use defaults.*/
Line 1,005: Line 1,420:
say 'unique characters are: ' @ /*display the unique characters found. */
say 'unique characters are: ' @ /*display the unique characters found. */
say
say
say 'Found ' L " unique characters." /*display the # of unique chars found. */</lang>
say 'Found ' L " unique characters." /*display the # of unique chars found. */</syntaxhighlight>
{{out|output|text=&nbsp; when using the default inputs:}}
{{out|output|text=&nbsp; when using the default inputs:}}
<pre>
<pre>
Line 1,014: Line 1,429:


=={{header|Ring}}==
=={{header|Ring}}==
<lang ring>
<syntaxhighlight lang="ring">
see "working..." + nl
see "working..." + nl
see "Unique characters are:" + nl
see "Unique characters are:" + nl
Line 1,047: Line 1,462:
end
end
return sum
return sum
</syntaxhighlight>
</lang>
{{out}}
{{out}}
<pre>
<pre>
Line 1,055: Line 1,470:
Found 8 unique characters
Found 8 unique characters
done...
done...
</pre>

=={{header|RPL}}==
{{works with|Halcyon Calc|4.2.7}}
≪ DUP SIZE → string length
≪ 1 length '''FOR''' n
string n DUP SUB
'''NEXT'''
length 1 '''FOR''' n
1 n 1 - START
'''IF''' DUP2 ≥ '''THEN''' SWAP '''END'''
n ROLLD
'''NEXT'''
n ROLLD
-1 '''STEP'''
2 length '''START''' + '''NEXT'''
≫ ≫
‘SORTS’ STO
≪ DUP 1 DUP SUB → str char1
≪ str SIZE
'''IF''' DUP 1 >
'''THEN'''
DROP 1
'''WHILE''' str OVER 1 + DUP SUB char1 == '''REPEAT''' 1 + '''END'''
'''END'''
char1
≫ ≫
‘OCHST’ STO
≪ "" 1 3 PICK SIZE '''FOR''' j
OVER j GET +
'''NEXT'''
SWAP DROP
SORTS "" SWAP 1
'''WHILE''' OVER SIZE OVER ≥ '''REPEAT'''
DUP2 OVER SIZE SUB OCHST
'''IF''' OVER 1 ==
'''THEN''' 5 ROLL SWAP + 4 ROLLD
'''ELSE''' DROP
'''END'''
+
'''END'''
DROP2
‘UNCHR’ STO
===Shorter code but increased memory requirements===
≪ → strings
≪ { 255 } 0 CON 1 strings SIZE '''FOR''' j
strings j GET 1 OVER SIZE '''FOR''' k
DUP k DUP SUB NUM ROT SWAP DUP2 GET 1 + PUT SWAP
'''NEXT'''
DROP
'''NEXT'''
"" 1 255 '''FOR''' j
'''IF''' OVER j GET 1 == '''THEN''' j CHR + '''END'''
'''NEXT'''
SWAP DROP
‘UNCHR’ STO

{"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"} UNCHR
{{out}}
<pre>
1: "156bgstz"
</pre>

=={{header|Ruby}}==
<syntaxhighlight lang="ruby">words = ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]

counter = words.inject({}){|h, word| word.chars.tally(h)}
puts counter.filter_map{|char, count| char if count == 1}.sort.join
</syntaxhighlight>
{{out}}
<pre>
156bgstz
</pre>
=={{header|Transd}}==
<syntaxhighlight lang="Scheme">#lang transd

MainModule: {
v: ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"],

_start: (λ
(for p in (group-by (split (join v "") "")) do
(if (== (size (snd p)) 1) (textout (fst p))))
)
}</syntaxhighlight>
{{out}}
<pre>
156bgstz
</pre>

=={{header|Standard ML}}==
Using an Array:
<syntaxhighlight lang="sml">
fun uniqueChars xs =
let
val arr = Array.array(256, 0)
val inc = (fn c => Array.update(arr, ord c, Array.sub(arr, ord c)+1))
val _ = List.app inc (List.concat (List.map String.explode xs))
val ex1 = (fn (i,n,a) => if n=1 then (chr i)::a else a)
in
String.implode (Array.foldri ex1 [] arr)
end
</syntaxhighlight>
{{out}}
<pre>
- uniqueChars ["133252abcdeeffd","a6789798st","yxcdfgxcyz"];
val it = "156bgstz" : string
</pre>

A different approach:
<syntaxhighlight lang="sml">
(*
group [1,1,2,4,4,4,2,2,2,1,1,1,3]
=> [[1,1], [2], [4,4,4], [2,2,2], [1,1,1], [3]]
*)
fun group xs =
let
fun collectGroups(a,[]) = [[a]]
| collectGroups(a,b::bs) = if a = (hd b) then (a::b)::bs else [a]::b::bs
in
List.foldr collectGroups [] xs
end

fun uniqueChars2 xs =
let
(* turn the strings into one big list of characters *)
val cs = List.concat (List.map String.explode xs)
(* sort the big list of characters *)
val scs = ListMergeSort.sort Char.> cs
(* collect the groups *)
val gs = group scs
(* filter out groups with more than one member *)
val os = List.filter (fn a => null (tl a)) gs
in
String.implode (List.concat os)
end
</syntaxhighlight>

=={{header|V (Vlang)}}==
<syntaxhighlight lang="v (vlang)">
fn main() {
strings := ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
mut m := map[rune]int{}
for s in strings {
for c in s {
m[c]++
}
}
mut chars := []rune{}
for k, v in m {
if v == 1 {chars << k}
}
chars.sort_with_compare(fn(i &rune, j &rune) int {
if *i < *j {return -1}
if *i > *j {return 1}
return 0
})
println(chars.string())
}
</syntaxhighlight>

{{out}}
<pre>
156bgstz
</pre>
</pre>


Line 1,060: Line 1,643:
{{libheader|Wren-seq}}
{{libheader|Wren-seq}}
{{libheader|Wren-sort}}
{{libheader|Wren-sort}}
<lang ecmascript>import "/seq" for Lst
<syntaxhighlight lang="wren">import "./seq" for Lst
import "/sort" for Sort
import "./sort" for Sort


var strings = ["133252abcdeeffd", "a6789798st","yxcdfgxcyz"]
var strings = ["133252abcdeeffd", "a6789798st","yxcdfgxcyz"]
Line 1,068: Line 1,651:
Sort.insertion(uniqueChars)
Sort.insertion(uniqueChars)
System.print("Found %(uniqueChars.count) unique character(s), namely:")
System.print("Found %(uniqueChars.count) unique character(s), namely:")
System.print(uniqueChars.join(" "))</lang>
System.print(uniqueChars.join(" "))</syntaxhighlight>


{{out}}
{{out}}
Line 1,075: Line 1,658:
1 5 6 b g s t z
1 5 6 b g s t z
</pre>
</pre>

=={{header|Yabasic}}==
{{trans|FreeBASIC}}
<syntaxhighlight lang="yabasic">dim c(255)
data "133252abcdeeffd", "a6789798st", "yxcdfgxcyz", ""
repeat
read s$
for i = 1 to len(s$)
a = asc(mid$(s$,i,1))
c(a) = c(a) + 1
next i
until s$ = ""

for i = 1 to 255
if c(i) = 1 then s$ = s$ + chr$(i) : fi
next i
print s$
end</syntaxhighlight>
{{out}}
<pre>
Igual que la entrada de FreeBASIC.
</pre>



=={{header|XPL0}}==
=={{header|XPL0}}==
<lang XPL0>int List, I, N, C;
<syntaxhighlight lang="xpl0">int List, I, N, C;
char Tbl(128), Str;
char Tbl(128), Str;
string 0;
string 0;
Line 1,093: Line 1,699:
for I:= 0 to 127 do
for I:= 0 to 127 do
if Tbl(I) = 1 then ChOut(0, I);
if Tbl(I) = 1 then ChOut(0, I);
]</lang>
]</syntaxhighlight>


{{out}}
{{out}}

Latest revision as of 20:03, 24 March 2024

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



11l

DefaultDict[Char, Int] d
L(s) [‘133252abcdeeffd’, ‘a6789798st’, ‘yxcdfgxcyz’]
   L(c) s
      d[c]++

L(k) sorted(d.keys())
   I d[k] == 1
      print(k, end' ‘’)
Output:
156bgstz

8080 Assembly

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
Output:
156bgstz

8086 Assembly

	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
Output:
156bgstz

Action!

DEFINE MAX="128"
CHAR ARRAY counts(MAX)

BYTE FUNC GetCount(CHAR ARRAY s CHAR c)
  BYTE count,i

  count=0
  FOR i=1 TO s(0)
  DO
    IF s(i)=c THEN
      count==+1
    FI
  OD
RETURN (count)

PROC UpdateCounts(CHAR ARRAY s)
  BYTE i,c

  FOR i=1 TO s(0)
  DO
    c=s(i)
    counts(c)==+GetCount(s,c)
  OD
RETURN

PROC Main()
  DEFINE PTR="CARD"
  DEFINE CNT="3"
  PTR ARRAY l(CNT)
  INT i

  l(0)="133252abcdeeffd"
  l(1)="a6789798st"
  l(2)="yxcdfgxcyz"

  SetBlock(counts,MAX,0)
  FOR i=0 TO CNT-1
  DO
    UpdateCounts(l(i))
  OD
  FOR i=0 TO MAX-1
  DO
    IF counts(i)=1 THEN
      Put(i) Put(32)
    FI
  OD
RETURN
Output:

Screenshot from Atari 8-bit computer

1 5 6 b g s t z

Ada

with Ada.Text_Io;

procedure Unique_Characters is

   List : array (Character) of Natural := (others => 0);

   procedure Count (Item : String) is
   begin
      for C of Item loop
         List (C) := List (C) + 1;
      end loop;
   end Count;

   procedure Put_Only_Once is
      use Ada.Text_Io;
   begin
      for C in List'Range loop
         if List (C) = 1 then
            Put (C);
            Put (' ');
         end if;
      end loop;
      New_Line;
   end Put_Only_Once;

begin
   Count ("133252abcdeeffd");
   Count ("a6789798st");
   Count ("yxcdfgxcyz");
   Put_Only_Once;
end Unique_Characters;
Output:
1 5 6 b g s t z

ALGOL 68

Case sensitive. This assumes a small character set (e.g. ASCII where max abs char is 255). Would probably need some work if CHAR is Unicode.

BEGIN # find the characters that occur once only in a list of stings        #
    # returns the characters that occur only once in the elements of s      #
    OP UNIQUE = ( []STRING s )STRING:
       BEGIN
            [ 0 : max abs char ]INT counts; # counts of used characters     #
            FOR i FROM LWB counts TO UPB counts DO counts[ 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 s[ i ] TO UPB s[ i ] DO
                    counts[ ABS s[ i ][ j ] ] +:= 1
                OD
            OD;
            # construct a string of the characters that occur only once     #
            STRING result := "";
            FOR i FROM LWB counts TO UPB counts DO
                IF counts[ i ] = 1 THEN result +:= REPR i FI
            OD;
            result
       END; # UNIQUE #
    # task test case                                                        #
    print( ( UNIQUE []STRING( "133252abcdeeffd",  "a6789798st",  "yxcdfgxcyz" ), newline ) )
END
Output:
156bgstz

AppleScript

AppleScriptObjC

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

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
Output:
{"1", "5", "6", "b", "g", "s", "t", "z"}

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.

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


Functional

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

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
Output:
{"1", "5", "6", "b", "g", "s", "t", "z"}

APL

uniques  (⍋⌷⊣)((/⍨)(1=(≢⊢)))
Output:
      uniques '133252abcdeeffd' 'a6789798st' 'yxcdfgxcyz'
156bgstz

Arturo

arr: ["133252abcdeeffd" "a6789798st" "yxcdfgxcyz"]
str: join arr

print sort select split str 'ch -> 1 = size match str ch
Output:
1 5 6 b g s t z

AWK

# syntax: GAWK -f UNIQUE_CHARACTERS.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("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)
}
Output:
133252abcdeeffd
a6789798st
yxcdfgxcyz
3 strings, 35 characters, 20 different, 8 unique: 156bgstz

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 ""
Output:
156bgstz

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)
$)
Output:
156bgstz

BQN

Uniq  (⍷/˜1=/)
Output:
   Uniq "133252abcdeeffd"‿"a6789798st"‿"yxcdfgxcyz"
"156bgstz"

C

#include <stdio.h>
#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;
}
Output:
156bgstz

C++

#include <iostream>
#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';
}
Output:
156bgstz

Delphi

Works with: Delphi version 6.0


var SA: array [0..2] of string = ('133252abcdeeffd',  'a6789798st',  'yxcdfgxcyz');

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;

procedure ShowUniqueChars(Memo: TMemo);
var I: integer;
var S: string;
begin
{Concatonate all strings}
S:='';
for I:=0 to High(SA) do S:=S+SA[I];
{Get all characters that appear once}
S:=CharsAppearingOnce(S);
Memo.Lines.Add(S);
end;
Output:
156bgstz

Elapsed Time: 0.959 ms.


Factor

Works with: Factor version 0.99 build 2074
USING: io sequences sets.extras sorting ;

{ "133252abcdeeffd" "a6789798st" "yxcdfgxcyz" }
concat non-repeating natural-sort print
Output:
156bgstz


FreeBASIC

Dim As Integer c(255), i, a
Dim As String s
Do
    Read s
    For i = 1 To Len(s)
        a = Asc(Mid(s,i,1))
        c(a) += 1
    Next i
Loop Until s = ""

For i = 1 To 255
    If c(i) = 1 Then s &= Chr(i)
Next i
Print s

Data "133252abcdeeffd", "a6789798st", "yxcdfgxcyz", ""
Sleep
Output:
156bgstz

FutureBasic

window 1, @"Unique characters"

void local fn DoIt
  CountedSetRef      set   = fn CountedSetWithCapacity(0)
  CFArrayRef         array = @[@"133252abcdeeffd",@"a6789798st",@"yxcdfgxcyz"]
  CFStringRef        string, chr
  long               index
  CFMutableArrayRef  mutArray = fn MutableArrayWithCapacity(0)
  
  for string in array
    for index = 0 to len(string) - 1
      CountedSetAddObject( set, mid(string,index,1) )
    next
  next
  
  for chr in set
    if ( fn CountedSetCountForObject( set, chr ) == 1 )
      MutableArrayAddObject( mutArray, chr )
    end if
  next
  
  MutableArraySortUsingSelector( mutArray, @"compare:" )
  
  print fn ArrayComponentsJoinedByString( mutArray, @"" )
end fn

fn DoIt

HandleEvents
Output:
156bgstz

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))
}
Output:
156bgstz

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"
      ]
Output:
156bgstz


Or folding the strings down to a hash of character frequencies:

import qualified Data.Map.Strict as M

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

uniqueChars :: [String] -> String
uniqueChars =
  M.keys . M.filter (1 ==)
    . foldr (M.unionWith (+) . charCounts) M.empty

charCounts :: String -> M.Map Char Int
charCounts =
  foldr
    (flip (M.insertWith (+)) 1)
    M.empty

--------------------------- TEST -------------------------
main :: IO ()
main =
  putStrLn $
    uniqueChars
      [ "133252abcdeeffd",
        "a6789798st",
        "yxcdfgxcyz"
      ]
Output:
156bgstz

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());
})();
Output:
["1","5","6","b","g","s","t","z"]


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

(() => {
    "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());
})();
Output:
["1","5","6","b","s","t","g","z"]

J

The simple approach here is to merge the argument strings and find characters which occur exactly once in that intermediate result:

uniques=: ~.#~1=#/.~

In other words, ~. finds the distinct characters, #/.~ finds the corresponding counts of those characters, so 1=#/.~ is true for the characters which occur exactly once, and #~ filters the distinct characters based on those truth values.

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

Here, ; as a separator between quoted strings builds a list of the strings, and ; as a prefix of that list merges the contents of the strings of that list into a single string. We could just as easily have formed a single string, but that's not what the task asked for. (Since uniques is a verb (aka a "function"), it's not a list element in this context.)

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.

# bag of words
def bow(stream): 
  reduce stream as $word ({}; .[($word|tostring)] += 1);

# Input: an array of strings
# Output: an array of the characters that appear just once
def in_one_just_once:
  bow( .[] | explode[] | [.] | implode) | with_entries(select(.value==1)) | keys;

The task

["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
| in_one_just_once
Output:
["1","5","6","b","g","s","t","z"]

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

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

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)
Output:

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

 1.010 μs (14 allocations: 1.05 KiB)


Lua

local strings = {"133252abcdeeffd",  "a6789798st",  "yxcdfgxcyz"}
unpack = unpack or table.unpack -- compatibility for all Lua versions

local map = {}
for i, str in ipairs (strings) do
	
	for i=1, string.len(str) do
		local char = string.sub(str,i,i)
		if map[char] == nil then
			map[char] = true
		else
			map[char] = false
		end
	end
	
end

local list = {}
for char, bool in pairs (map) do
	if bool then
		table.insert (list, char)
	end
end
table.sort (list)
print (unpack (list))
Output:
1	5	6	b	g	s	t	z

Mathematica/Wolfram Language

Select[Tally[Sort[Characters[StringJoin[{"133252abcdeeffd", "a6789798st", "yxcdfgxcyz"}]]]], Last /* EqualTo[1]][[All, 1]]
Output:
{"1", "5", "6", "b", "g", "s", "t", "z"}

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

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)
Output:
@['1', '5', '6', 'b', 'g', 's', 't', 'z']

Pascal

Works with: Extended Pascal
program uniqueCharacters(output);

type
	{ `integer…` prefix to facilitate sorting in documentation tools }
	integerNonNegative = 0..maxInt;
	{ all variables of this data type will be initialized to `1` }
	integerPositive = 1..maxInt value 1;
	
	charFrequency = array[char] of integerNonNegative;
	
	{ This “discriminates” the `string` _schema_ data type. }
	line = string(80);
	{ A schema data type definition looks sort of like a function: }
	lines(length: integerPositive) = array[1..length] of line;

{ `protected` in Extended Pascal means, you can’t modify this parameter. }
function createStatistics(protected sample: lines): charFrequency;
var
	i, n: integerPositive;
	{ The `value …` clause will initialize this variable to the given value. }
	statistics: charFrequency value [chr(0)..maxChar: 0];
begin
	{ You can inspect the discriminant of schema data type just like this: }
	for n := 1 to sample.length do
	begin
		{ The length of a `string(…)` though needs to be queried: }
		for i := 1 to length(sample[n]) do
		begin
			statistics[sample[n, i]] := statistics[sample[n, i]] + 1
		end
	end;
	
	{ There needs to be exactly one assignment to the result variable: }
	createStatistics := statistics
end;

{ === MAIN ============================================================= }
var
	c: char;
	sample: lines(3) value [1: '133252abcdeeffd';
		2: 'a6789798st'; 3: 'yxcdfgxcyz'];
	statistics: charFrequency;
begin
	statistics := createStatistics(sample);
	
	for c := chr(0) to maxChar do
	begin
		{
			The colon (`:`) specifies the display width of the parameter.
			While for `integer` and `real` values it means the _minimum_ width,
			for `string` and `char` values it is the _exact_ width.
		}
		write(c:ord(statistics[c] = 1))
	end;
	
	writeLn
end.
Output:
156bgstz

Perl

Translation of: Raku
# 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 )
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

(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" ) ) )
Output:
("1" "5" "6" "b" "g" "s" "t" "z")

PL/M

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
Output:
156BGSTZ

Python

'''Unique characters'''

from itertools import chain, groupby


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


# ------------------------- TEST -------------------------
# main :: IO ()
def main():
    '''Characters occurring only once
       across a list of 3 given strings.
    '''
    print(
        uniques([
            "133252abcdeeffd",
            "a6789798st",
            "yxcdfgxcyz"
        ])
    )


# MAIN ---
if __name__ == '__main__':
    main()
Output:
['1', '5', '6', 'b', 'g', 's', 't', 'z']

Or reducing the given strings down to a hash of character frequencies:

'''Unique characters'''

from functools import reduce


# uniqueChars :: [String] -> [Char]
def uniqueChars(ws):
    '''Characters which occur only once
        across the given list of strings.
    '''
    def addedWord(dct, w):
        return reduce(updatedCharCount, w, dct)

    def updatedCharCount(a, c):
        return dict(
            a, **{
                c: 1 + a[c] if c in a else 1
            }
        )

    return sorted([
        k for k, v in reduce(addedWord, ws, {}).items()
        if 1 == v
    ])


# ------------------------- TEST -------------------------
# main :: IO ()
def main():
    '''Test'''
    print(
        uniqueChars([
            "133252abcdeeffd",
            "a6789798st",
            "yxcdfgxcyz"
        ])
    )


# MAIN ---
if __name__ == '__main__':
    main()
Output:
['1', '5', '6', 'b', 'g', 's', 't', 'z']

Quackery

  [ [] swap
    witheach join
    [] 0 128 of
    rot witheach
      [ 2dup peek
        1+ unrot poke ]
    witheach
      [ 1 = if
          [ i^ join ] ] ] is task ( [ --> $ )

 []
 $ "133252abcdeeffd" nested join
 $ "a6789798st"      nested join
 $ "yxcdfgxcyz"      nested join

 task echo$
Output:
156bgstz

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.

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

/*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. */
output   when using the default inputs:
unique characters are:   1 5 6 b g s t z

Found  8  unique characters.

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
Output:
working...
Unique characters are:
1 5 6 b g s t z 
Found 8 unique characters
done...

RPL

Works with: Halcyon Calc version 4.2.7
≪ DUP SIZE → string length
  ≪ 1 length FOR n
        string n DUP SUB
    NEXT
    length 1 FOR n 
       1 n 1 - START 
         IF DUP2 ≥ THEN SWAP END 
          n ROLLD 
      NEXT 
       n ROLLD   
   -1 STEP
   2 length START + NEXT
≫ ≫
‘SORTS’ STO

≪ DUP 1 DUP SUB → str char1
  ≪ str SIZE 
    IF DUP 1 > 
    THEN 
       DROP 1 
       WHILE str OVER 1 + DUP SUB char1 == REPEAT 1 + END
    END
    char1  
≫ ≫
‘OCHST’ STO

≪ "" 1 3 PICK SIZE FOR j 
     OVER j GET + 
  NEXT 
  SWAP DROP
  SORTS "" SWAP 1 
  WHILE OVER SIZE OVER ≥ REPEAT 
     DUP2 OVER SIZE SUB OCHST 
     IF OVER 1 == 
     THEN 5 ROLL SWAP + 4 ROLLD 
     ELSE DROP 
     END 
     + 
  END 
  DROP2 
≫ 
‘UNCHR’ STO

Shorter code but increased memory requirements

≪ → strings 
  ≪ { 255 } 0 CON 1 strings SIZE FOR j 
     strings j GET 1 OVER SIZE FOR k 
        DUP k DUP SUB NUM ROT SWAP DUP2 GET 1 + PUT SWAP 
      NEXT 
      DROP 
   NEXT 
   ≫ 
 "" 1 255 FOR j
    IF OVER j GET 1 == THEN j CHR + END 
  NEXT 
  SWAP DROP 
≫
‘UNCHR’ STO
{"133252abcdeeffd",  "a6789798st",  "yxcdfgxcyz"} UNCHR
Output:
1: "156bgstz"

Ruby

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

counter = words.inject({}){|h, word| word.chars.tally(h)}
puts counter.filter_map{|char, count| char if count == 1}.sort.join
Output:
156bgstz

Transd

#lang transd

MainModule: {
    v: ["133252abcdeeffd",  "a6789798st",  "yxcdfgxcyz"],

    _start: (λ
        (for p in (group-by (split (join v "") "")) do
            (if (== (size (snd p)) 1) (textout (fst p))))
    )
}
Output:
156bgstz

Standard ML

Using an Array:

fun uniqueChars xs =
  let
    val arr = Array.array(256, 0)
    val inc = (fn c => Array.update(arr, ord c, Array.sub(arr, ord c)+1))
    val _ = List.app inc (List.concat (List.map String.explode xs))
    val ex1 = (fn (i,n,a) => if n=1 then (chr i)::a else a)
  in
    String.implode (Array.foldri ex1 [] arr)
  end
Output:
- uniqueChars ["133252abcdeeffd","a6789798st","yxcdfgxcyz"];
val it = "156bgstz" : string

A different approach:

(*
  group [1,1,2,4,4,4,2,2,2,1,1,1,3]
  => [[1,1], [2], [4,4,4], [2,2,2], [1,1,1], [3]]
*)
fun group xs =
  let
    fun collectGroups(a,[]) = [[a]]
      | collectGroups(a,b::bs) = if a = (hd b) then (a::b)::bs else [a]::b::bs
  in
    List.foldr collectGroups [] xs
  end

fun uniqueChars2 xs =
  let
    (* turn the strings into one big list of characters *)
    val cs = List.concat (List.map String.explode xs)
    (* sort the big list of characters *)
    val scs = ListMergeSort.sort Char.> cs
    (* collect the groups *)
    val gs = group scs
    (* filter out groups with more than one member *)
    val os = List.filter (fn a => null (tl a)) gs
  in
    String.implode (List.concat os)
  end

V (Vlang)

fn main() {
    strings := ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
    mut m := map[rune]int{}
    for s in strings {
        for c in s {
            m[c]++
        }
    }
    mut chars := []rune{}
    for k, v in m {
        if v == 1 {chars << k}
    }
	chars.sort_with_compare(fn(i &rune, j &rune) int {
		if *i < *j {return -1}
		if *i > *j {return 1}
		return 0
	})
	println(chars.string())
}
Output:
156bgstz

Wren

Library: Wren-seq
Library: Wren-sort
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(" "))
Output:
Found 8 unique character(s), namely:
1 5 6 b g s t z

Yabasic

Translation of: FreeBASIC
dim c(255)
data "133252abcdeeffd", "a6789798st", "yxcdfgxcyz", ""
repeat
    read s$
    for i = 1 to len(s$)
        a = asc(mid$(s$,i,1))
        c(a) = c(a) + 1
    next i
until s$ = ""

for i = 1 to 255
    if c(i) = 1 then s$ = s$ + chr$(i) : fi
next i
print s$
end
Output:
Igual que la entrada de FreeBASIC.


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);
]
Output:
156bgstz