Unique characters
- Task
Given a list of strings, find characters appearing only in one string and once only.
The result should be given in alphabetical order.
Let list = ["133252abcdeeffd", "a6789798st", "yxcdfgxcyz"]
Factor
<lang factor>USING: io sequences sets.extras sorting ;
{ "133252abcdeeffd" "a6789798st" "yxcdfgxcyz" } concat non-repeating natural-sort print</lang>
- Output:
156bgstz
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']
3.133 μs (32 allocations: 3.80 KiB) 5.349 μs (52 allocations: 3.28 KiB)
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
<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