42
\$\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\$
  • 6
    \$\begingroup\$ Just to double check: Does the no builtins rule mean that set objects are disallowed? \$\endgroup\$ – Sp3000 Oct 4 '15 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 Oct 4 '15 at 14:54
  • 5
    \$\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 Oct 4 '15 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 Oct 4 '15 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 Apr 6 '19 at 2:26

67 Answers 67

3
\$\begingroup\$

Simplex v.0.7, 22 10 bytes

s^oR^l^Ryg
s          ~~ string input
 ^o        ~~ convert to tuple
   R       ~~ go right
    ^l     ~~ unicode tuple
      ^R   ~~ take intersection
        yg ~~ convert to string and output

sigh Minkolang is kicking Simplex's butt.

s^[oZ%_Q]n?[^_s^~M^=]]
s                      ~~ string input
 ^[     ]              ~~ postfixes each of the inner with a ^
   o                   ~~ convert to tuple
    Z                ] ~~ fold
     %                 ~~ tuple w/out current character + increment
      _                ~~ current character
       Q               ~~ membership
         n?[        ]  ~~ if current character not in tuple
            ^_s^~M^=   ~~ print that character 

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ What a coup for Simplex! \$\endgroup\$ – El'endia Starman Oct 28 '15 at 2:51
2
\$\begingroup\$

AutoIt, 107

Func _($0)
$1=''
For $2 In StringSplit($0,'',3)
$1&=StringInStr($1,$2,1)?'':$2
Next
Return $1
EndFunc

Call it like any function:

ConsoleWrite(_("Type unique chars!"))
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

CJam, 9

Lq{1$-+}/

This doesn't convert a string to a set, but it performs a kind of set difference to determine whether a character is found in a string. Try it online

Explanation:

L       push an empty array/string
q       read the input
{…}/    for each character in the input
  1$    copy the previous string
  -     subtract from the character (set difference),
         resulting in the character or empty string
  +     append the result to the string

Another version, 13 bytes:

Lq{_2$#)!*+}/

This doesn't do anything related to sets. Try it online

Explanation:

L       push an empty array/string
q       read the input
{…}/    for each character in the input
  _     duplicate the character
  2$    copy the previous string
  #)    find the index of the character in the string and increment it
  !     negate, resulting in 0 if the character was in the string and 1 if not
  *     repeat the character that many times
  +     append the result to the string
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Haskell, 50

f=concat.(tail>>=zipWith(\x y->[last x]\\y)).inits
| improve this answer | |
\$\endgroup\$
  • 3
    \$\begingroup\$ inits is in Data.List, so you need an import, which is usually added to the byte count. \$\endgroup\$ – nimi Oct 4 '15 at 20:46
2
\$\begingroup\$

Matlab, 46 bytes

It uses an anonymous function, with function arguments as input and output:

@(s)eval('s(~any(triu(bsxfun(@eq,s,s''),1)))')

(I couldn't get this to work in an Octave online interpreter.)

Example use:

>> @(s)eval('s(~any(triu(bsxfun(@eq,s,s''),1)))')
ans = 
    @(s)eval('s(~any(triu(bsxfun(@eq,s,s''),1)))')

>> ans('Type unique chars!')
ans =
Type uniqchars!
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ that would have been my idea as well :) - you don't need the ,1 with any, btw. \$\endgroup\$ – Jonas Oct 4 '15 at 20:57
  • \$\begingroup\$ @Jonas Thanks! Alrhough it's hard to see through that mess of parentheses, the 1 is for triu (I need to remove the diagonal), not for any \$\endgroup\$ – Luis Mendo Oct 4 '15 at 22:36
2
\$\begingroup\$

Delphi, 111 bytes

procedure u(var s:string);var i:word;begin for i:=length(s) downto 1 do if pos(s[i],s)<i then delete(s,i,1)end;

Using the procedure to alter the passed in string - saves a few bytes in comparison to declaring the function. Moving backwards through the string, delete the current character if it is not the first location of that character

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6) 46

f=s=>[...s].map(c=>s[c]?'':s[c]=c,s=[]).join``

Unexpectedly, this time the flag array is one byte shorter than the indexOf approach.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

SED, 61 bytes

s/^/\a/
:
s/\a\(\(.\).*\)\2/\a\1/
t
s/\a\(.\)/\1\a/
t
s/\a//
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 2 characters shorter: s/\a\(.\)\(.*\)\1/\a\1\2/s/\a\(\(.\).*\)\2/\a\1/. If you have GNU sed you can remove the parenthesis escaping at the price of +1 for the -r switch. \$\endgroup\$ – manatwork Oct 6 '15 at 13:43
  • \$\begingroup\$ @manatwork: Nice, changed. (I could also shrink all \a sequences into a single unprintable BEL character, though I'm not sure it's really worth it) \$\endgroup\$ – Hasturkun Oct 6 '15 at 14:08
2
\$\begingroup\$

Perl 5.10+, 21 bytes

perl -pe '1while s/(.).*\K\1//'

20 bytes + 1 byte for -p. Note that Perl 5.10 or above is required for \K.

Takes input from stdin:

$ echo 'Type unique chars!' | perl -pe '1while s/(.).*\K\1//'
Type uniqchars!

$ echo \"I think it\'s dark and it looks like rain\", you said | perl -pe '1while s/(.).*\K\1//'
"I think'sdarloe,yu

$ echo 3.1415926535897932384626433832795 | perl -pe '1while s/(.).*\K\1//'
3.14592687

Online demo


Inspired by NinjaBearMonkey's Retina answer. Perl may not have a compact way to re-run a substitution until nothing changes, but it does have \K, which causes the regex engine to "keep" everything to its left. This allows you to drop a set of parentheses on the LHS and a capture variable on the RHS:

s/(.).*\K\1//   # 13 bytes

vs.

s/((.).*)\2/$1/ # 15 bytes
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Save 1 byte using s/(.).*\K\1//&&redo \$\endgroup\$ – Ton Hospel Sep 28 '16 at 11:50
2
\$\begingroup\$

Minkolang 0.10, 13 bytes

This language was made after this challenge but not for this challenge.

Much thanks to Sp3000 for pointing me the way to this much shorter solution.

od?.dd0q?Od0p

Try it here.

Explanation

od               Read character from input and duplicate it (for the conditional)
  ?.             Stop if 0 (i.e., input is empty)
    dd           Duplicate twice (for q and for possible output)
      0q         Gets value stored in (character,0) in the codebox (0 if empty)
        ?O       Outputs character if it's unique (i.e. the previous value was 0)
          d0p    Puts the value of the character at (value,0) in the codebox
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

AppleScript, 143 142 Bytes

set x to(display dialog""default answer"")'s text returned's characters
set o to""
repeat with i in x
if not i is in o then set o to o&i
end
o

Not a language for golfing, but it's very readable.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Octave, 25 bytes

@(s)s(sum(triu(s'==s))<2)
| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Javascript (ES6), 47 bytes

a=>[...a].filter((b,c)=>a.indexOf(b)==c).join``

Test it here

Some code is added to have a better input and to write to the console the result.

d=document,g=d.getElementById.bind(d);g("s").onclick=e=>console.log((a=>[...a].filter((b,c)=>a.indexOf(b)==c).join``)(g("t").value))
<input id="t"><button id="s">Convert</button>

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Vim, 23 keystrokes

qq:s/\v(.).*\zs\1<cr>@qq@q

Explanation:

qq                           " Start recording into register 'q'
  :s/                        " Search and replace:
      \v                     " Enable 'magic', which just makes regexes a littl shorter
        (.)                  " Any symbol, captured in group one
           .*                " Anything
             \zs\1           " Start selection, group one
                             " Replace with nothing
                  <cr>       " Globally
                      @q     " Call macro q. Since we are still recording, this will not do anything the first time, and create a loop the next time
                        q    " Stop recording. 
                         @q  " Start the loop. This will run until there is no match.

Since V is backwards compatible, you can Try it online!

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Ruby, 19 + 1 = 20 bytes

Uses the -p flag. Uses set operations like the GolfScript answer, which is totally not a built-in like uniq. (Performing set or on a list and an empty list purges the list of duplicates, just like calling and against a copy of itself.)

$_=($_.chars|[])*""
| improve this answer | |
\$\endgroup\$
2
\$\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.

| improve this answer | |
\$\endgroup\$
  • \$\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 Jun 9 at 23:05
2
\$\begingroup\$

Scala, 118114 bytes

def f(a:String):String=a.zipWithIndex.groupBy(_._1.toLower).map(_._2.head).toList.sortBy(_._2).map(_._1).mkString
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Python 2, 79

def S(s):
 s=s[::-1]
 for c in s:s=s.replace(c,'',s.count(c)-1)
 return s[::-1]

It reverses the input string, cycles through it, replaces any extra characters with null ones and returns the reverse of that. The reverse is so the last characters are removed, not the first.

>>> S('''"I think it's dark and it looks like rain", you said''')
'"I think\'sdarloe,yu'
>>> S("3.1415926535897932384626433832795")
'3.14592687'
>>> S("Type unique chars!")
'Type uniqchars!'
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 68 bytes \$\endgroup\$ – nope Jun 10 at 11:48
1
\$\begingroup\$

Haskell, 33 bytes

foldl(\r c->r++[c|notElem c r])[]

Uses foldl to iterate through the string appending characters that are not in the current result, starting with the empty string (list).

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

MUMPS, 54 bytes

f(s) f i=1:1:$L(s) s c=$E(s,i) s:t'[c o=o_c,t=t_c
    q o

Not terribly exciting - it just keeps a running list of already-viewed characters and only appends to the output string if the current character isn't in that list.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Powershell, 30

$args-replace'(.)(?<=\1.+)',''

I feel there's likely to be a better regex than this, and I can't work out what it is

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Rust, 318 Bytes

not really a competitor, but rust needs a bit more love. As always, comments and advice welcome.

use std::io;fn main(){let mut s=io::stdin();let mut b=String::new();s.read_line(&mut b).unwrap();let mut n: Vec<char>=b.chars().collect();for i in 0..n.len()-2{let mut j=i+1;let mut p = true;while j<n.len(){if j<n.len(){if n[i]==n[j]{n.remove(j);p=false;}}if p{j+=1;}p=true;}}for i in 0..n.len()-2{print!("{}",n[i]);}}

Try it online here (first compile, then execute, then click into the proper area to enter user input)

ungolfed:

use std::io;
fn main() {
let mut s = io::stdin();
let mut b=String::new();
s.read_line(&mut b).unwrap();
let mut chars: Vec<char> = b.chars().collect();
for i in 0..chars.len()-2 {
    let mut j=i+1;
    let mut inc = true;
    while j<chars.len() {
        if j<chars.len() 
        {
            if chars[i]==chars[j] {
                chars.remove(j);
                inc = false;
            }
        }
        if inc {
            j+=1;
        }
        inc = true;
    }
}
for i in 0..chars.len()-2 {
    print!("{}",chars[i]);
}
}
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Python 2, 62 bytes

s=set();print filter(lambda i:not(i in s or s.add(i)),input())
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Python 3, 132 109 105 bytes

u=[]
def t(l):
 if l in u:return''
 else:
  u.append(l);return l
print(''.join([t(l)for l in input()]))

Ungolfed:

used_chars = []
def test(letter):
    if letter in used_chars:
        return '' # skip
    else:
        used_chars.append(letter)
        return letter
print(''.join([test(letter) for letter in input()]))
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Shouldn't the r(l) in the last line be t(l)? \$\endgroup\$ – user4768 Oct 5 '15 at 9:16
  • \$\begingroup\$ Also, you can use print if you end the line with a comma: print ('%s') % (''.join([r(l)for l in i])), then import sys won't be necessary. \$\endgroup\$ – user4768 Oct 5 '15 at 9:19
  • \$\begingroup\$ Following Thomas Kwa's suggestion, one trailing \n is acceptable in stdout (in fact it's implicit in many functions used for displaying into stdout). I've made this explicit in the challenge rules. You may perhaps use that to reduce your code length \$\endgroup\$ – Luis Mendo Oct 5 '15 at 9:54
1
\$\begingroup\$

Scala, 79, 66 bytes

Inspired by the Haskell version using foldLeft

def f(s:String)=s.foldLeft("")((a,c)=>if(a.contains(c))a else a+c)

Previous version using zipWithIndex and collect

def f(s:String)=s.zipWithIndex.collect{case(c,i)if s.indexOf(c)==i=>c}.mkString
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Perl 6, 32 bytes

The way you would normally write this would be:

$*IN.comb.unique.print # it would have been 22

Which would have had the benefit of being obviously correct.


Instead I have to write something like the following

print $*IN.comb.grep:{!.{$^a}++} # 32
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Gema, 18 characters

?=${?;$0}@set{$0;}

Sample run:

bash-4.3$ gema '?=${?;$0}@set{$0;}' <<< 'hello world'
helo wrd
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

R, 52 bytes

cat(union(a<-strsplit(readline(),"")[[1]],a),sep="")

Test examples:

Type uniqchars!
"I think'sdarloe,yu
3.14592687
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

RProgN, 85 Bytes, Non Competing

S i 'a' = s 'n' = a L while a pop 'b' = n b hasvalue ! if n b push end a L end n i ++

Explanation

S i                     # Take the implicit string input, convert it to a stack such that the first letter is at the bottom, invert the stack such that the first letter is at the top.
'a' =                   # Associate it with 'a'
s 'n' =                 # Create a new stack, associate it with 'n'
a L                     # Push the size of a
while                   # While the top of the stack contains a truthy value
    a pop 'b' =         # Pop the top of a, associate it with 'b'
    n b hasvalue !      # Push if the stack 'n' does not contain b
    if                  # If the top of the stack is truthy
        n b push        # Push b to the stack n
    end                 #
    a L                 # Push the length of a
end                     # loop
n i ++                  # Push the n, inverts it, sums it, which acts to concatenate it, implicitly print the result.

Non-Competing as the 'Not' and 'HasValue' functions were both not implemented prior to this challenge.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

REXX, 79 bytes

a=arg(1)
o=
do while a>''
  parse var a b+1 a
  if pos(b,o)=0 then o=o||b
  end
say o
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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