49
\$\begingroup\$

Given a string consisting of printable ASCII chars, produce an output consisting of its unique chars in the original order. In other words, the output is the same as the input except that a char is removed if it has appeared previously.

No built-ins for finding unique elements in an array can be used (for example, MATLAB has a unique function that does that). The idea is to do it manually.

Further details:

  • Either functions or programs are allowed.
  • Input and output can be in the form of function arguments, stdin/stdout (even for functions), or a mix of those.
  • If stdin or stdout are used, a string is understood as just the sequence of chars. If function arguments are used, the sequence of chars may need to be enclosed in quotation marks or equivalent symbols that the programming language of choice uses for defining strings.
  • The output should be a string containing only the unique characters of the input. So no extra linebreaks, spaces etc. The only exception is: if the output is displayed in stdout, most displaying functions add a trailing \n (to separate the string from what will come next). So one trailing \n is acceptable in stdout.
  • If possible, post a link to an online interpreter/compiler so that others can try your code.

This is code golf, so shortest code in bytes wins.

Some examples, assuming stdin and stdout:

  1. Input string:

    Type unique chars!
    

    Output string:

    Type uniqchars!
    
  2. Input string

    "I think it's dark and it looks like rain", you said
    

    Output string

    "I think'sdarloe,yu
    
  3. Input string

    3.1415926535897932384626433832795
    

    Output string

    3.14592687
    
\$\endgroup\$
16
  • 8
    \$\begingroup\$ Just to double check: Does the no builtins rule mean that set objects are disallowed? \$\endgroup\$
    – Sp3000
    Commented Oct 4, 2015 at 14:51
  • \$\begingroup\$ @Sp3000 Set objects are allowed. Just don't use a function or method (if it exists) that gives you its unique elements. And input/output should be strings, not set tobjects \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 14:54
  • 7
    \$\begingroup\$ I think only my answer uses sets currently, and I wouldn't mind if you changed it. However, I'm not really sure a bonus like that would change much, e.g. I doubt CJam would be doable in < 6 bytes without sets. Also, I'm not sure where the line is between a builtin which finds unique elements, and constructing a set from a number of elements... \$\endgroup\$
    – Sp3000
    Commented Oct 4, 2015 at 15:13
  • 2
    \$\begingroup\$ @Sp3000 Yes, it's a blurred border. I hadn't anticipated set functions. I think I'll leave the challenge as it is now \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 15:16
  • 1
    \$\begingroup\$ -1 Disallowing built-ins serves no purpose other than making interesting answers. This is code-golf, not popularity contests. \$\endgroup\$
    – MilkyWay90
    Commented Apr 6, 2019 at 2:26

90 Answers 90

24
\$\begingroup\$

CJam, 3 bytes

qL|

Setwise or of the input with an empty list. CJam set operations preserve element order.

Try it online

\$\endgroup\$
2
  • \$\begingroup\$ I'm assuming this is valid since sets are allowed, but I'm not sure... \$\endgroup\$
    – Sp3000
    Commented Oct 4, 2015 at 14:58
  • \$\begingroup\$ Very clever! I knew CJam would be one of the best, but I didn't expect just 3 bytes! \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 14:59
20
\$\begingroup\$

C# 6, 18 + 67 = 85 bytes

Requires this using statement:

using System.Linq;

The actual method:

string U(string s)=>string.Concat(s.Where((x,i)=>s.IndexOf(x)==i));

This method saves some chars by defining the function as a lambda, which is supported in C# 6. This is how it would look in C# pre-6 (but ungolfed):

string Unique(string input)
{
    return string.Concat(input.Where((x, i) => input.IndexOf(x) == i));
}

How it works: I call the Where method on the string with a lambda with two arguments: x representing the current element, i representing the index of that element. IndexOf always returns the first index of the char passed to it, so if i is not equal to the first index of x, it's a duplicate char and mustn't be included.

\$\endgroup\$
5
  • 3
    \$\begingroup\$ I honestly wouldn't have expected C# to be this short. Excellent job! \$\endgroup\$
    – Alex A.
    Commented Oct 4, 2015 at 17:40
  • \$\begingroup\$ Uhm. I think you should submit a complete program (with static void Main etc.). \$\endgroup\$
    – Timwi
    Commented Oct 5, 2015 at 10:49
  • 4
    \$\begingroup\$ @Timwi This challenge states "Either functions or programs are allowed." \$\endgroup\$
    – hvd
    Commented Oct 5, 2015 at 14:22
  • \$\begingroup\$ C# allows for a shorter approach, also using LINQ. I've posted a competing answer. :) \$\endgroup\$
    – hvd
    Commented Oct 5, 2015 at 14:37
  • \$\begingroup\$ @hvd Nice one! +1 \$\endgroup\$
    – ProgramFOX
    Commented Oct 5, 2015 at 15:19
18
\$\begingroup\$

GolfScript, 2 bytes

.&

or, alternatively:

.|

I posted this a while ago in the Tips for golfing in GolfScript thread. It works by duplicating the input string (which is put on the stack automatically by the GolfScript interpreter, and which behaves in most ways like an array of characters) and then taking the set intersection (&) or union (|) of it with itself. Applying a set operator to an array (or string) collapses any duplicates, but preserves the order of the elements.

\$\endgroup\$
0
15
\$\begingroup\$

Retina, 14 bytes

+`((.).*)\2
$1

Each line should go in its own separate file, or you can use the -s flag to read from one file.

To explain it, we'll use this longer but simpler version:

+`(.)(.*)\1
$1$2

The first line is the regex to match with (+` is the configuration string that keeps running until all replacements have been made). The regex looks for a character (we'll call it C), followed by zero or more arbitrary characters, followed by C. The parentheses denote capturing groups, so we replace the match with C ($1) and the characters in between ($2), removing the duplicate of C.

For example, if the input string was unique, the first run would match uniqu, with u and niq as $1 and $2, respectively. It would then replace the matched substring in the original input with uniq, giving uniqe.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ I was looking for a regex to do this; I didn't realize it was so short! +1 \$\endgroup\$ Commented Oct 4, 2015 at 22:54
13
\$\begingroup\$

Perl 5 + -p, 19 bytes

Saved 1 byte thanks to @kos and 1 byte thanks to @Xcali!

s/./$&x!$h{$&}++/eg

Try it online!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ You could save 1 byte negating $h{$&} and using a logic AND instead of a ternary operator: s/./!$h{$&}++&&$&/eg \$\endgroup\$
    – kos
    Commented Oct 5, 2015 at 12:55
  • \$\begingroup\$ @kos if you'd asked me, I'd have told you that I 100% tried this and ended up with 1s in the output, but it doesn't! Thank you, updating! \$\endgroup\$ Commented Oct 5, 2015 at 15:51
  • 1
    \$\begingroup\$ Upvoted already :) I think you tried s/./$h{$&}++||$&/eg (I fell for that as well at first). Shame because that would have been another saved byte. \$\endgroup\$
    – kos
    Commented Oct 5, 2015 at 16:03
  • 1
    \$\begingroup\$ Got it down one more byte: Try it online! \$\endgroup\$
    – Xcali
    Commented Jan 15, 2021 at 7:07
  • \$\begingroup\$ @Xcali Nice one! Thanks! \$\endgroup\$ Commented Jan 15, 2021 at 10:34
12
\$\begingroup\$

JavaScript ES7, 37 33 25 bytes

Pretty simple approach using ES6 Set and ES7 Array comprehensions spread operator:

s=>[...new Set(s)].join``

22 bytes less than the indexOf approach. Worked on a handful of test cases.

\$\endgroup\$
8
  • \$\begingroup\$ The spaces around for's expression are not necessary and you could make it anonymous function as some other solutions did: s=>[for(c of Set(s))c].join``. (Pale update: not 100% sure, but the new keyword seems also unnecessary.) \$\endgroup\$
    – manatwork
    Commented Oct 5, 2015 at 10:36
  • \$\begingroup\$ Wasn't sure of the rules with anon functions, and good catch on the space. \$\endgroup\$
    – azz
    Commented Oct 5, 2015 at 10:37
  • \$\begingroup\$ Transpiled code without new resulted in Uncaught TypeError: Constructor Set requires 'new' in Google Chrome. \$\endgroup\$
    – azz
    Commented Oct 5, 2015 at 10:43
  • \$\begingroup\$ Please excuse my ignorance but at what point does this filter unique values? It looks like it just converts a string to a set to an array then joins the values resulting in the original string again. \$\endgroup\$ Commented Oct 5, 2015 at 14:17
  • \$\begingroup\$ @PatrickRoberts it's the conversion to a set. A set by definition has no dupluicates \$\endgroup\$
    – edc65
    Commented Oct 5, 2015 at 14:32
11
\$\begingroup\$

Macaroni 0.0.2, 233 bytes

set i read set f "" print map index i k v return label k set x _ set _ slice " " length index f e 1 1 set f concat f wrap x return label e set _ slice " " add _ multiply -1 x 1 1 return label v set _ unwrap slice i _ add 1 _ 1 return
  • create "anti-golfing" language: check
  • golf it anyway: check

This is a full program, which inputs from STDIN and outputs on STDOUT.

Wrapped version, for aesthetic value:

set i read set f "" print map index i k v return label k set x _ set _ slice "
" length index f e 1 1 set f concat f wrap x return label e set _ slice " " add
_ multiply -1 x 1 1 return label v set _ unwrap slice i _ add 1 _ 1 return

And a heavily "commented" and ungolfed version (there are no comments in Macaroni, so I just use bare string literals):

set input read                  "read line from STDIN, store in 'input' var"
set found ""                    "we need this for 'keep' below"
print map index input keep val  "find indeces to 'keep', map to values, print"
return

label keep
    "we're trying to determine which indeces in the string to keep. the special
     '_' variable is the current element in question, and it's also the value
     to be 'returned' (if the '_' variable is '0' or empty array after this
     label returns, the index of the element is *not* included in the output
     array; otherwise, it is"
    set x _ set _ slice
        " "
        length index found exists
        1
        1
    "now we're using 'index' again to determine whether our '_' value exists in
     the 'found' array, which is the list of letters already found. then we
     have to apply a boolean NOT, because we only want to keep values that do
     NOT exist in the 'found' array. we can 'invert' a boolean stored as an
     integer number 'b' (hence, 'length') with 'slice(' ', b, 1, 1)'--this is
     equivalent to ' '[0:1], i.e. a single-character string which is truthy, if
     'b' was falsy; otherwise, it results in an empty string if 'b' was truthy,
     which is falsy"
    set found concat found wrap x  "add the letter to the 'found' array"
return

label exists
    set _ slice
        " "
        add _ multiply -1 x
        1
        1
    "commentary on how this works: since 0 is falsy and every other number is
     truthy, we can simply subtract two values to determine whether they are
     *un*equal. then we apply a boolean NOT with the method described above"
return

label val
    set _ unwrap slice input _ add 1 _ 1  "basically 'input[_]'"
return

(This is the first real Macaroni program (that actually does something)! \o/)

\$\endgroup\$
1
  • 5
    \$\begingroup\$ • give the language a funny and appropriate name: check \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 15:32
9
\$\begingroup\$

Beam, 23 18 bytes

v<H
vs)
rS
g@S
>u^

enter image description here

Try it online!

\$\endgroup\$
0
8
\$\begingroup\$

Python 2, 42 bytes

Uses a couple anonymous functions and reduce.

lambda s:reduce(lambda x,y:x+y[y in x:],s)

Try it online

\$\endgroup\$
0
8
\$\begingroup\$

C# 6 - 18+46=64

using System.Linq;

and then

string f(string s)=>string.Concat(s.Union(s));

The Enumerable.Union extension method specifies that elements are returned in the original order:

When the object returned by this method is enumerated, Union enumerates first and second in that order and yields each element that has not already been yielded.

Set operations that aren't specifically intended to find unique values appear to be allowed judging by the other answers.

\$\endgroup\$
6
  • \$\begingroup\$ Nice one, I was thinking of string u(string s)=>String.Join("",s.Distinct()); but that's a little bit longer. \$\endgroup\$
    – germi
    Commented Oct 7, 2015 at 12:41
  • \$\begingroup\$ @germi Thanks. There has been an answer using Distinct() already, but it's deleted because Distinct() is disallowed in this challenge, as it's a method specifically intended to find unique values. \$\endgroup\$
    – hvd
    Commented Oct 7, 2015 at 12:43
  • \$\begingroup\$ Ah right... overlooked that bit ;) \$\endgroup\$
    – germi
    Commented Oct 7, 2015 at 12:47
  • \$\begingroup\$ Is s => string.Concat(s.Union(s)) valid? That would be the delegate passed to a Func<string, string> as an argument. \$\endgroup\$ Commented Sep 28, 2016 at 3:17
  • \$\begingroup\$ @TylerStandishMan If that's valid, I would expect more people to make use of it, and I haven't seen it before, so I don't think it is. But maybe it should be valid -- this seems like something worth checking on Meta if you're interested. \$\endgroup\$
    – hvd
    Commented Sep 28, 2016 at 5:02
7
\$\begingroup\$

JavaScript ES6, 47 bytes

f=s=>s.replace(/./g,(e,i)=>s.indexOf(e)<i?'':e)

The test below works on all browsers.

f=function(s){
  return s.replace(/./g,function(e,i){
    return s.indexOf(e)<i?'':e
  })
}

run=function(){document.getElementById('output').innerHTML=f(document.getElementById('input').value)};document.getElementById('run').onclick=run;run()
<input type="text" id="input" value="Type unique chars!" /><button id="run">Run</button><br />
<pre id="output"></pre>

\$\endgroup\$
2
  • \$\begingroup\$ What does the <i?'':e part do? \$\endgroup\$
    – DanTheMan
    Commented Oct 7, 2015 at 1:50
  • 1
    \$\begingroup\$ It's a ternary operator. If the first instance of a character e is before the current index i, it returns an empty string, thus getting rid of the character. If that is the first instance, it simply returns e and no changes are made. \$\endgroup\$ Commented Oct 7, 2015 at 3:03
7
\$\begingroup\$

MATLAB, 23

 @(n)union(n,n,'stable')

Does the "set union" of the input string with itself, using the 'stable' method which does not sort, and then prints.

This works because union returns only non-duplicate values after the merge. So essentially if you union the string with itself, it first produces a string like Type unique chars!Type unique chars!, and then removes all duplicates without sorting.

No need for unique :)

\$\endgroup\$
7
  • \$\begingroup\$ unique not allowed, sorry! It's in the challange definition \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 15:28
  • \$\begingroup\$ Missed that, never mind. \$\endgroup\$ Commented Oct 4, 2015 at 15:31
  • \$\begingroup\$ Following Sp3000's answer, may I suggest setdiff with the 'stable' option? \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 15:39
  • 1
    \$\begingroup\$ Nice! And yes, you can remove disp because then you have a function that returns a string , which is allowed \$\endgroup\$
    – Luis Mendo
    Commented Oct 4, 2015 at 16:20
  • 1
    \$\begingroup\$ You can also use intersect with 'stable' to achieve the same effect too. I was going to write that, but given this answer, it's no longer original lol. \$\endgroup\$
    – rayryeng
    Commented Oct 4, 2015 at 17:24
7
\$\begingroup\$

><>, 16 bytes

i:0(?;:::1g?!o1p

><> doesn't have strings, so we make use of the codebox. Due to the toroidal nature of ><>, the following runs in a loop:

i         Read a char
:0(?;     Halt if EOF
:::       Push three copies of the char
1g        Get the value at (char, 1), which is 0 by default
?!o       Print the char if the value was nonzero
1p        Set the value at (char, 1) to char

Note that this uses the fact that the input only contains printable ASCII, as this would not work if ASCII 0 was present.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ .......this is brilliant. I wish I had thought of this. I'll include a Befunge version of this in my answer, but not as the primary. EDIT: On second thought, this wouldn't work because Befunge doesn't have an infinite code space. Dangit! \$\endgroup\$ Commented Oct 5, 2015 at 5:52
  • \$\begingroup\$ @El'endiaStarman I think the Beam answer also does the same thing, so unfortunately I can't say I was first :P \$\endgroup\$
    – Sp3000
    Commented Oct 5, 2015 at 5:53
  • \$\begingroup\$ Ahh, yeah, I think you're right. Your explanation is clearer though. \$\endgroup\$ Commented Oct 5, 2015 at 6:03
7
\$\begingroup\$

Python 3, 44

r=''
for c in input():r+=c[c in r:]
print(r)

Builds the output string r character by character, including the character c from the input only if we haven't seen it already.

Python 2 would be 47, losing 4 chars with raw_input and saving 1 on not needing parers for print.

\$\endgroup\$
1
  • \$\begingroup\$ The consensus now seems to be that you can use input in Python 2, so you can make yours a byte shorter. \$\endgroup\$
    – mbomb007
    Commented Sep 28, 2016 at 13:24
6
\$\begingroup\$

Element, 22 19 18 bytes

_'{"(3:~'![2:`];'}

Example input/output: hello world -> helo wrd

This works by simply processing the string one character at a time and keeping track which ones it has seen before.

_'{"(3:~'![2:`];'}
_                        input line
 '                       use as conditional
  {              }       WHILE loop
   "                     retrieve string back from control (c-) stack
    (                    split to get the first character of (remaining) string
     3:                  a total of three copies of that character
       ~                 retrieve character's hash value
        '                put on c-stack
         !               negate, gives true if undef/empty string
          [   ]          FOR loop
           2:`           duplicate and output
               ;         store character into itself
                '        put remaining string on c-stack as looping condition
\$\endgroup\$
6
\$\begingroup\$

APL, 3

∊∪/

This applies union (∪) between each element of the vector, obtaining an iteration that has the effect of removing duplicates.

Test it on tryapl.org

Old One:

~⍨\

This uses ~ (with reversed arguments, using ⍨) applied between each element of the argument. The result is that for each element, if it's already in the list, it gets erased.

\$\endgroup\$
2
  • \$\begingroup\$ Nitpicking: "And input/output should be strings" says Luis. "Unione reduce" returns a nested array, not a string. O:-) \$\endgroup\$
    – lstefano
    Commented Jun 28, 2016 at 11:28
  • \$\begingroup\$ You're right, adding a ∊ at the beginning to correct. \$\endgroup\$ Commented Jun 28, 2016 at 14:42
5
\$\begingroup\$

TI-BASIC, 49 bytes

Input Str1
"sub(Str1,X,1→Y₁
Y₁(1
For(X,2,length(Str1
If not(inString(Ans,Y₁
Ans+Y₁
End
Ans

The equation variables are rarely useful since they take 5 bytes to store to, but Y₁ comes in handy here as the Xth character of the string, saving 3 bytes. Since we can't add to empty strings in TI-BASIC, we start the string off with the first character of Str1, then loop through the rest of the string, adding all characters not already encountered.

prgmQ
?Why no empty st
rings? Because T
I...
Why noemptysrig?Bcau.
\$\endgroup\$
5
\$\begingroup\$

Pyth, 7 bytes

soxzN{z

Pseudocode:

z = input

sum of order-by index in z of N over set of z.

\$\endgroup\$
5
\$\begingroup\$

Julia, 45 42 bytes

s->(N="";[i∈N?N:N=join([N,i])for i=s];N)

Old version:

s->(N={};for i=s i∈N||(N=[N,i])end;join(N))

Code builds the new string by appending new characters onto it, then joins them together to a proper string at the end. New version saves some characters by iterating via array comprehension. Also saves a byte by using ?: rather than || (as it removes the need for brackets around the assignment).

Alternate solution, 45 bytes, using recursion and regex:

f=s->s!=(s=replace(s,r"(.).*\K\1",""))?f(s):s

Julia, 17 bytes

(Alternate version)

s->join(union(s))

This uses union as basically a substitute for unique - I don't consider this the "real" answer, as I interpret "don't use unique" to mean "don't use a single built-in function that has the effect of returning the unique elements".

\$\endgroup\$
2
  • \$\begingroup\$ I had a similar idea but it wasn't as concise. Nice work! \$\endgroup\$
    – Alex A.
    Commented Oct 4, 2015 at 17:38
  • \$\begingroup\$ 35 bytes [tio.run/##yyrNyUw0rPj/… Try it online!] \$\endgroup\$
    – amelies
    Commented Nov 7, 2022 at 9:10
4
\$\begingroup\$

Ruby, 30 24 characters

(23 characters code + 1 character command line option.)

gsub(/./){$`[$&]?"":$&}

Sample run:

bash-4.3$ ruby -pe 'gsub(/./){$`[$&]?"":$&}' <<< 'hello world'
helo wrd
\$\endgroup\$
4
\$\begingroup\$

Perl, 54 27 bytes

map{$h{$_}||=print}<>=~/./g
123456789012345678901234567

Test:

$ echo Type unique chars! | perl -e 'map{$h{$_}||=print}<>=~/./g'
Type uniqchars!
$
\$\endgroup\$
6
  • 1
    \$\begingroup\$ print exists($h{$_})?"":$_$h{$_}||print \$\endgroup\$
    – manatwork
    Commented Oct 4, 2015 at 16:25
  • \$\begingroup\$ Has SO inserted a unicode → char in there rendering it broken? \$\endgroup\$
    – steve
    Commented Oct 4, 2015 at 16:29
  • 1
    \$\begingroup\$ using a statement modifier would save you some bytes, along with @manatwork's suggestion $h{$_}||=print and using <>=~/./g should help save a few more as well! \$\endgroup\$ Commented Oct 4, 2015 at 16:30
  • 1
    \$\begingroup\$ No, I inserted it, with the meaning of “change to”. \$\endgroup\$
    – manatwork
    Commented Oct 4, 2015 at 16:30
  • 1
    \$\begingroup\$ Changing to map would also improve the saving: map{$h{$_}||=print}<>=~/./g \$\endgroup\$
    – manatwork
    Commented Oct 4, 2015 at 16:32
4
\$\begingroup\$

PHP, 72 Bytes 84 Bytes

<?foreach(str_split($argv[1])as$c)$a[$c]=0;echo join('',array_keys($a));

Uses the characters as keys for an associative array, then prints the keys. Order of array elements is always the order of insertion.

Thanks Ismael Miguel for the str_split suggestion.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ <?foreach(str_split($argv[1])as$c)$a[$c]=0;echo join('',array_keys($a)); Shorter and does the same. \$\endgroup\$ Commented Oct 4, 2015 at 20:27
  • \$\begingroup\$ Found a shorter loop: while($c=$argv[1][$i++*1]). This replaces the whole foreach. Everything else is the same \$\endgroup\$ Commented Oct 4, 2015 at 20:43
  • \$\begingroup\$ I tried something similar first but refrained from it because it would stop on a character that coerces to "false", i.e. "0". Try "abc0def" as input. \$\endgroup\$ Commented Oct 4, 2015 at 20:47
  • \$\begingroup\$ You're right about it. Surelly there's a workaround for it that doesn't cost more than 2 bytes. \$\endgroup\$ Commented Oct 4, 2015 at 20:48
4
\$\begingroup\$

Java, 78 bytes

String f(char[]s){String t="";for(char c:s)t+=t.contains(c+"")?"":c;return t;}

A simple loop while checking the output for characters already present. Accepts input as a char[].

\$\endgroup\$
4
\$\begingroup\$

Haskell, 29 bytes

Nestable, no-variable-name one-liner:

foldr(\x->(x:).filter(x/=))[]

Same count, saved into a function named f as a top-level declaration:

f(x:t)=x:f[y|y<-t,x/=y];f_=[]

Note that there is a slightly-cheating optimization which I haven't made in the spirit of niceness: it is technically still allowed by the rules of this challenge to use a different input and output encoding for a string. By representing any string by its partially-applied Church encoding \f -> foldr f [] string :: (a -> [b] -> [b]) -> [b] (with the other side of the bijection provided by the function ($ (:))) this gets golfed down to ($ \x->(x:).filter(x/=)), only 24 characters.

I avoided posting the 24-character response as my official one because the above solution could be tried on the above interpreter as foldr(\x->(x:).filter(x/=))[]"Type unique chars!"whereas the golfed solution would be written instead:

($ \x->(x:).filter(x/=))$ foldr (\x fn f->f x (fn f)) (const []) "Type unique chars!"

as a shorthand for the literal declaration which would be the more-insane:

($ \x->(x:).filter(x/=))$ \f->f 'T'.($f)$ \f->f 'y'.($f)$ \f->f 'p'.($f)$ \f->f 'e'.($f)$ \f->f ' '.($f)$ \f->f 'u'.($f)$ \f->f 'n'.($f)$ \f->f 'i'.($f)$ \f->f 'q'.($f)$ \f->f 'u'.($f)$ \f->f 'e'.($f)$ \f->f ' '.($f)$ \f->f 'c'.($f)$ \f->f 'h'.($f)$ \f->f 'a'.($f)$ \f->f 'r'.($f)$ \f->f 's'.($f)$ \f->f '!'.($f)$ const[]

But it's a perfectly valid version of the data structure represented as pure functions. (Of course, you can use \f -> foldr f [] "Type unique chars!" too, but that is presumably illegitimate since it uses lists to actually store the data, so its foldr part should then presumably be composed into the "answer" function, leading to more than 24 characters.)

\$\endgroup\$
4
\$\begingroup\$

Python 3, 26 bytes

lambda s:[*dict(zip(s,s))]

Try it online!

Reviving an ancient question with a new idea, inspired by Noodle9's solution to "Special String reformatting" and my further refinement.

Because Python 3's dictionaries automatically remove duplicate keys beyond the first appearance while keeping them in order, we convert the the input string to a dictionary as keys. We then convert the dictionary back to a list with iterable unpacking, which extracts its keys. (The challenge seems to be fine with a list of characters in lieu of a string, asking for a "sequence of chars".)

While we could make the dictionary via a comprehension like {c:1for c in s}, it's one byte shorter to use the dict constructor as dict(zip(s,s)), which each character is both the key and the values. A same-length alternative is {}.fromkeys(s).

Note that while set does also remove duplicates, it fails to keep them in order, and is also disallowed by the challenge.

\$\endgroup\$
1
  • \$\begingroup\$ Great explanation. I could follow it perfectly with my limited Python knowledge. Yes, any sequence of chars is fine, not necessarily a string. By the way, set operations are allowed, and in fact some answers use them. (In retrospect, forbidding builtins was not a great idea anyway. It was my first challenge, and I guess in 2015 we were more permissive with that) \$\endgroup\$
    – Luis Mendo
    Commented Jun 9, 2020 at 23:05
4
\$\begingroup\$

05AB1E, 5 4 bytes

ʒkNQ

Try it online or verify all test cases.

Slightly more interesting previous 5 bytes version:

SkāαÏ

Try it online or verify all test cases.

Explanation:

ʒ     # Filter the characters in the (implicit) input-string by:
 k    #  Get the first index of the current character in the (implicit) input-string
  NQ  #  Check if it's equal to the filter-index
      # (after which the result is output implicitly)

S     # Convert the (implicit) input-string to a list of characters
 k    # Get the first 0-based index of each character in the (implicit) input-string
  ā   # Push a list in the range [1, list-length] (without popping)
   α  # Get the absolute difference between the values at the same positions in the lists
    Ï # Keep the characters of the (implicit) input-string at the truthy (==1) indices
      # (after which the result is output implicitly)

With builtins allowed, this would of course be a single byte: Ù
Try it online or verify all test cases.

\$\endgroup\$
4
\$\begingroup\$

GNU AWK, 35 34 bytes

BEGIN{RS=".|";ORS=e}!a[RT]++,$0=RT

Try it online!

This is a major edit of my first version. In fact, it changed entirely. Only works in GNU AWK, due to the RT variable.

BEGIN{RS=".|";ORS=e}

RS=".|" works like the /./ regex, so every character of the input is considered a record separator. The practical use is that RT returns each character at a time. ORS=e prevents newlines from appearing.

!a[RT]++,$0=RT

A simple AND gate. This is a range pattern. The left part: current RT element of the a array is incremented by 1, but it is evaluated by the NOT gate before the increment. Returns TRUE only the first time a character appears. The right part: assigns the current character (RT) to the input/output $0; only happens when beginning pattern is true, i.e., the first time a character appears.

AWK, 48 bytes (former answer)

split($0,a,e){for(i in a)printf a[j=a[i]]++?e:j}

Try it online!

{
split($0,a,e); # Splits the input into the _a_ array, one char for each element.
               # _e_ variable is not assigned, and returns "".

for(i in a)            # For each element _i_ of the array _a_,
  printf
         a[j=a[i]]++?  # if the element a[i] (AKA j) is positive (i.e, true),
                  ^--- (This also increments this element by 1 after evaluating it)
                   e:  # prints _e_ (AKA "")
                   j   # if it's zero (AKA not assigned, AKA false), prints a[i].
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You can shave off one ; by moving the split($0,a,e) to the left of the {. \$\endgroup\$
    – cnamejj
    Commented Jan 16, 2021 at 9:16
  • \$\begingroup\$ @cnamejj - you're right! Thanks for the tip. \$\endgroup\$ Commented Jan 16, 2021 at 11:14
4
\$\begingroup\$

C - 56

Thanks to @ceilingcat, @hvd and @AShelly for saving a bunch of characters. There were multiple ways suggested of making it much shorter than the original:

// @ceilingcat      - put vars in for loop
j(char*s){for(char*q=s,*p=s;*q=*p;q+=q==index(s,*p++));}

// @ceilingcat      - use index() instead of strchr()
j(char*s){char*q=s,*p=s;for(;*q=*p;q+=q==index(s,*p++));}

// @hvd             - modify in place
i(char*s){char*q=s,*p=s;for(;*q=*p;q+=q==strchr(s,*p++));}

// @AShelly         - keep a histogram of the usage of each character
h(char*s){int a[128]={0};for(;*s;s++)a[*s]++||putchar(*s);}

// @hvd             - always copy to q but only increment q if not found
g(char*s,char*r){char*q=r;for(;*q=*s;q+=q==strchr(r,*s++));}

// original version - requires -std=c99
void f(char*s,char*r){for(char*q=r;*s;s++)if(!strchr(r,*s))*q++=*s;}

As you can see modifying in place seems to be the shortest (so far!) The test program compiles without warnings using gcc test.c

#include <stdlib.h> // calloc
#include <string.h> // strchr
#include <stdio.h>  // puts, putchar

// 000000111111111122222222223333333333444444444455555555556666666666
// 456789012345678901234567890123456789012345678901234567890123456789

// @hvd     - always copy to q but only increment q if not found
g(char*s,char*r){char*q=r;for(;*q=*s;q+=q==strchr(r,*s++));}

// @AShelly - keep a histogram of the usage of each character
h(char*s){int a[128]={0};for(;*s;s++)a[*s]++||putchar(*s);}

// @hvd     - modify in place
i(char*s){char*q=s,*p=s;for(;*q=*p;q+=q==strchr(s,*p++));}

/* original version - commented out because it requires -std=c99
void f(char*s,char*r){for(char*q=r;*s;s++)if(!strchr(r,*s))*q++=*s;}
*/

// The test program:
int main(int argc,char*argv[]){
  char *r=calloc(strlen(argv[1]),1); // make a variable to store the result
  g(argv[1],r);                      // call the function
  puts(r);                           // print the result

  h(argv[1]);                        // call the function which prints result
  puts("");                          // print a newline

  i(argv[1]);                        // call the function (modifies in place)
  puts(argv[1]);                     // print the result
}

Thanks for all the help. I appreciate all the advice given to shorten so much!

\$\endgroup\$
11
  • \$\begingroup\$ Well, since your code already isn't valid C, just accepted by lenient C compilers: you can declare r as int (and omit the int) to save some bytes: f(s,r)char*s;{...}. But it limits your code to platforms where char* is the same size as int, and of course where compilers are as lenient as yours and mine. \$\endgroup\$
    – hvd
    Commented Oct 6, 2015 at 9:00
  • \$\begingroup\$ @hvd That's evil! I was willing to default the return value because I don't use it. But that's a bit more dodgy than I would like to be. I think I would prefer making it compliant rather than going that far! Thanks for bringing back to the light side. \$\endgroup\$ Commented Oct 6, 2015 at 9:07
  • \$\begingroup\$ You can save one char by replacing if(x)y with x?y:0 \$\endgroup\$
    – ugoren
    Commented Oct 6, 2015 at 12:17
  • \$\begingroup\$ Here's a 60 char function that writes to stdout instead of an array parameter: f(char*s){int a[128]={0};for(;*s;s++)a[*s]++?0:putchar(*s);} \$\endgroup\$
    – AShelly
    Commented Oct 6, 2015 at 17:29
  • \$\begingroup\$ You can unconditionally copy into *q, and only increment q if the character appeared earlier, allowing stuffing a bit more together: void f(char*s,char*r){for(char*q=r;*q=*s;strchr(r,*s++)<q||q++);} (Note that strchr(r,*s++)<q is always well-defined, there's no UB there, because strchr cannot return NULL in this version.) Except for the return type, it's even shorter than @AShelly's version. \$\endgroup\$
    – hvd
    Commented Oct 6, 2015 at 17:30
3
\$\begingroup\$

Java, 79 bytes

It's java, and it may be a little bit shorter than some of the more wordy esoteric languages.

But still, java.

s->{String r="";for(char c:s.toCharArray())r+=(r.indexOf(c)>-1)?"":c;return r;}

Explanation:

s->{ //Lambda expression opening, note that java can infer the type of s

    String r=""; //Initializes String r

    for(char c:s.toCharArray()) //Iterates through the string

        r+=(r.indexOf(c)>-1)?"":c; //indexOf(c) returns -1 if there are no occurrences

    return r;

}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I know it's been about 2.5 years, but you can golf it to s->{String r="";for(String c:s.split(""))r+=r.indexOf(c)<0?c:"";return r;}, or even six bytes shorter when using Java 10 instead of 8, so both String can be var. And I'm not 100% sure it's allowed, but I think you can just input it as a String-array instead so .split("") can be removed. In total: s->{var r="";for(var c:s)r+=r.indexOf(c)<0?c:"";return r;} Try it online 58 bytes. (Or alternatively: s->{var r="";for(var c:s)r=r.indexOf(c)<0?r+c:r;return r;} Also 58 bytes.) \$\endgroup\$ Commented Apr 6, 2018 at 14:52
3
\$\begingroup\$

Befunge-93, 124 bytes

v
<v1p02-1
0_v#`g00: <0_@#+1::~p
 1>:1+10p2+0g-!#v_v
g `#v_10g0^       >:10g00
 ^0g 00$        <
 ^  >:,00g1+:00p1+:1+01-\0p

Test it in this online interpreter.


This was more difficult than I expected. I'll post a fuller explanation tomorrow if anyone wants me to, but here's an overview of what my code does.

  • Unique characters seen so far are stored on the first row, starting from 2,0 and extending to the right. This is checked against to see if the current character is a duplicate.
  • The number of unique characters seen so far is stored in 0,0 and the check-for-duplicate loop counter is stored in 1,0.
  • When a unique character is seen, it is stored on the first row, printed out, and the counter in 0,0 is incremented.
  • To avoid problems with reading in the spaces present (ASCII 32), I put the character corresponding to -1 (really, 65536) in the next slot for the next unique character.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.