12
\$\begingroup\$

I came upon this question, because it seems to be very common use-case to find unique characters in string. But what if we want to get rid of them?

Input contains only lower case alphabets. Only letters from a to z are used. Input length may be from 1 to 1000 characters.

Example:
input: helloworld
output: llool

Objective: Shortest code wins
Language: Any of the top 20 of TIOBE languages

\$\endgroup\$

27 Answers 27

7
\$\begingroup\$

Perl, 28 24 characters (includes 1 for 'p' option)

s/./$&x(s!$&!$&!g>1)/eg

Usage:

> perl -pe 's/./$&x(s!$&!$&!g>1)/eg'
helloworld
llool

At first I thought I could do this with negative look-ahead and negative look-behind, but it turns out that negative look-behinds must have a fixed length. So I went for nested regexes instead. With thanks to mob for the $& tip.

\$\endgroup\$
  • \$\begingroup\$ +1. I naively thought I could take this thing with my Ruby answer. \$\endgroup\$ – Steven Rumbalski Oct 11 '12 at 14:27
  • \$\begingroup\$ i tried this on chinese text and it did not do the trick. =( \$\endgroup\$ – ixtmixilix Oct 12 '12 at 0:12
  • \$\begingroup\$ @ixtmixilix - then run perl with the -CDS option \$\endgroup\$ – mob Oct 12 '12 at 0:38
  • \$\begingroup\$ @ixtmixilix I don't know enough about unicode and Perl's support of it to suggest a way to make it work with chinese text I'm afraid. Luckily for me the question says only lower case a to z. \$\endgroup\$ – Gareth Oct 12 '12 at 0:38
  • 1
    \$\begingroup\$ Replace all the $1 with $& and you can lose a couple pairs of parentheses. \$\endgroup\$ – mob Oct 12 '12 at 0:39
12
\$\begingroup\$

(GolfScript, 15 13 characters)

:;{.;?);>?)},

GolfScript is not one of the top 20, but a codegolf without GolfScript... (run it yourself)

Previous Version: (run script)

1/:;{;\-,;,(<},
\$\endgroup\$
  • 1
    \$\begingroup\$ :;? You're deliberately trying to confuse newbies, aren't you? ;) \$\endgroup\$ – Peter Taylor Oct 11 '12 at 9:17
  • \$\begingroup\$ @PeterTaylor You're right. I should have chosen a ) - it would make it a smiley then :). Unfortunately, I didn't find a way to even eliminate the digit 1. (Note for GolfScript newbies: you may replace any ; in the code with a x (or any other letter or digit - or any character not used in the script otherwise). In this special case ; is just a variable name - and has not the meaning "pop and discard". In GolfScript almost all tokens are variables anyways, and using predefined symbols is great way to make scripts even more unreadable for outsiders ;-).) \$\endgroup\$ – Howard Oct 11 '12 at 11:09
  • \$\begingroup\$ Another 13-char solution: :a{]a.@--,(}, \$\endgroup\$ – Ilmari Karonen Apr 11 '14 at 19:01
7
\$\begingroup\$

J, 12 characters

Having entered a valid Perl answer, here's an invalid (language not in the TIOBE top 20) answer.

a=:#~1<+/@e.

Usage:

   a 'helloworld'
llool

Declares a verb a which outputs only non unique items.

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

GolfScript (14 chars)

:x{{=}+x\,,(},

Online demo

Might not qualify to win, but it's useful to have a yardstick.

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

Ruby 46 40 36

gets.chars{|c|$><<c if$_.count(c)>1}
\$\endgroup\$
  • \$\begingroup\$ You may save 4 chars if you inline s and use $_ for the second appearance (the space before is then dispensable). \$\endgroup\$ – Howard Oct 11 '12 at 11:22
  • \$\begingroup\$ @Howard: Nice catch. Thanks. I have about zero experience with Ruby. \$\endgroup\$ – Steven Rumbalski Oct 11 '12 at 14:03
2
\$\begingroup\$

Perl 44

$l=$_;print join"",grep{$l=~/$_.*$_/}split""

Execution:

perl -lane '$l=$_;print join"",grep{$l=~/$_.*$_/}split""' <<< helloworld
llool
\$\endgroup\$
2
\$\begingroup\$

K, 18

{x@&x in&~1=#:'=x}
\$\endgroup\$
  • \$\begingroup\$ You can save a byte using 1<# instead of ~1=# \$\endgroup\$ – J. Sendra Feb 25 at 21:00
2
\$\begingroup\$

Python 2.7 (52 51), Python 3 (52)

I didn't expect it to be so short.

2.7: a=raw_input();print filter(lambda x:a.count(x)>1,a)

3.0: a=input();print''.join(i for i in a if a.count(x)>1)

raw_input(): store input as a string (input() = eval(raw_input()))
(Python 3.0: input() has been turned into raw_input())

filter(lambda x:a.count(x)>1,a): Filter through all characters within a if they are found in a more than once (a.count(x)>1).

\$\endgroup\$
  • \$\begingroup\$ If you use python 3 instead, you can use input() rather than raw_input(). Although you have to add one character for a closing bracket, since print is a function in python 3. \$\endgroup\$ – Strigoides Oct 16 '12 at 2:03
  • \$\begingroup\$ @Strigoides: I have added a Python 3 code snippet to my answer. \$\endgroup\$ – beary605 Oct 16 '12 at 2:18
  • \$\begingroup\$ Python 3's filter returns an iterator... You'll need to do ''.join(...) \$\endgroup\$ – JBernardo Oct 16 '12 at 4:23
  • \$\begingroup\$ @JBernardo: :( Dang. Thanks for notifying me. As you can see, I don't use 3.0. \$\endgroup\$ – beary605 Oct 16 '12 at 5:20
2
\$\begingroup\$

sed and coreutils (128)

Granted this is not part of the TIOBE list, but it's fun (-:

<<<$s sed 's/./&\n/g'|head -c -1|sort|uniq -c|sed -n 's/^ *1 (.*)/\1/p'|tr -d '\n'|sed 's:^:s/[:; s:$:]//g\n:'|sed -f - <(<<<$s)

De-golfed version:

s=helloworld
<<< $s sed 's/./&\n/g'        \
| head -c -1                  \
| sort                        \
| uniq -c                     \
| sed -n 's/^ *1 (.*)/\1/p'   \
| tr -d '\n'                  \
| sed 's:^:s/[:; s:$:]//g\n:' \
| sed -f - <(<<< $s)

Explanation

The first sed converts input into one character per line. The second sed finds characters that only occur once. Third sed writes a sed script that deletes unique characters. The last sed executes the generated script.

\$\endgroup\$
2
\$\begingroup\$

Brachylog (v2), 8 bytes

⊇.oḅlⁿ1∧

Try it online!

Function submission. Technically noncompeting because the question has a limitation on what langauges are allowed to compete (however, several other answers have already ignored the restriction).

Explanation

⊇.oḅlⁿ1∧
⊇         Find {the longest possible} subset of the input
  o       {for which after} sorting it,
   ḅ        and dividing the sorted input into blocks of identical elements,
    lⁿ1     the length of a resulting block is never 1
 .     ∧  Output the subset in question.
\$\endgroup\$
  • \$\begingroup\$ Why do you CW all your solutions? \$\endgroup\$ – Shaggy Feb 25 at 20:11
  • 1
    \$\begingroup\$ @Shaggy: a) because I'm fine with other people editing them, b) to avoid gaining reputation if they're upvoted. In general I think the gamififcation of Stack Exchange is a huge detriment to the site – there's sometimes a negative correlation between the actions that you can take to improve rep and the actions you can take to actually improve the site. Additionally, being at a high reputation count sucks; the site keeps nagging you to do admin tasks, and everything you do is a blunt instrument (e.g. when you're at low rep you can suggest an edit, at high rep it just gets forced through). \$\endgroup\$ – ais523 Feb 25 at 20:24
2
\$\begingroup\$

Japt, 6 5 bytes

ÆèX É

-1 byte thanks to @Oliver

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ Welcome to Japt! There is actually a shortcut for o@: Æ \$\endgroup\$ – Oliver Feb 25 at 18:29
  • \$\begingroup\$ @Oliver Another shortcut that I missed, cool, thanks :) \$\endgroup\$ – Quintec Feb 25 at 20:13
  • \$\begingroup\$ @Oliver, the better question is how the feck did I miss it?! :\ \$\endgroup\$ – Shaggy Feb 27 at 18:23
1
\$\begingroup\$

Python (56)

Here's another (few chars longer) alternative in Python:

a=raw_input();print''.join(c for c in a if a.count(c)>1)

If you accept output as a list (e.g. ['l', 'l', 'o', 'o', 'l']), then we could boil it down to 49 characters:

a=raw_input();print[c for c in a if a.count(c)>1]
\$\endgroup\$
  • \$\begingroup\$ Hey, >1 is a good idea! May I incorporate that into my solution? \$\endgroup\$ – beary605 Oct 11 '12 at 2:32
  • \$\begingroup\$ @beary605 Sure no problem at all - easy way to trim a character off :D \$\endgroup\$ – arshajii Oct 11 '12 at 2:43
1
\$\begingroup\$

Mathematica 72 63

Ok, Mathematica isn't among the top 20 languages, but I decided to join the party anyway.

x is the input string.

"" <> Select[y = Characters@x, ! MemberQ[Cases[Tally@y, {a_, 1} :> a], #] &]
\$\endgroup\$
1
\$\begingroup\$

Perl (55)

@x=split//,<>;$s{$_}++for@x;for(@x){print if($s{$_}>1)}

Reads from stdin.

\$\endgroup\$
1
\$\begingroup\$

C# – 77 characters

Func<string,string>F=s=>new string(s.Where(c=>s.Count(d=>c==d)>1).ToArray());

If you accept the output as an array, it boils down to 65 characters:

Func<string,char[]>F=s=>s.Where(c=>s.Count(d=>c==d)>1).ToArray();
\$\endgroup\$
1
\$\begingroup\$

Ocaml, 139 133

Uses ExtLib's ExtString.String

open ExtString.String
let f s=let g c=fold_left(fun a d->a+Obj.magic(d=c))0 s in replace_chars(fun c->if g c=1 then""else of_char c)s

Non-golfed version

open ExtString.String
let f s =
  let g c =
    fold_left
      (fun a c' -> a + Obj.magic (c' = c))
      0
      s
  in replace_chars
  (fun c ->
    if g c = 1
    then ""
    else of_char c)
  s

The function g returns the number of occurences of c in the string s. The function f replaces all chars either by the empty string or the string containing the char depending on the number of occurences. Edit: I shortened the code by 6 characters by abusing the internal representation of bools :-)

Oh, and ocaml is 0 on the TIOBE index ;-)

\$\endgroup\$
  • \$\begingroup\$ f*** the TIOBE index. \$\endgroup\$ – ixtmixilix Oct 12 '12 at 0:04
  • \$\begingroup\$ I agree. Also, thanks for the upvote. Now I can comment :-) \$\endgroup\$ – ReyCharles Oct 12 '12 at 0:17
1
\$\begingroup\$

PHP - 70

while($x<strlen($s)){$c=$s[$x];echo substr_count($s,$c)>1?$c:'';$x++;}

with asumption $s = 'helloworld'.

\$\endgroup\$
1
\$\begingroup\$

Java 8, 90 bytes

s->{for(char c=96;++c<123;s=s.matches(".*"+c+".*"+c+".*")?s:s.replace(c+"",""));return s;}

Explanation:

Try it online.

s->{                         // Method with String as both parameter and return-type
  for(char c=96;++c<123;     //  Loop over the lowercase alphabet
    s=s.matches(".*"+c+".*"+c+".*")?
                             //   If the String contains the character more than once
       s                     //    Keep the String as is
      :                      //   Else (only contains it once):
       s.replace(c+"",""));  //    Remove this character from the String
  return s;}                 //  Return the modified String
\$\endgroup\$
1
\$\begingroup\$

PowerShell, 59 bytes

"$args"-replace"[^$($args|% t*y|group|?{$_.Count-1}|% n*)]"

Try it online!

Less golfed:

$repeatedСhars=$args|% toCharArray|group|?{$_.Count-1}|% name
"$args"-replace"[^$repeatedСhars]"

Note: $repeatedChars is an array. By default, a Powershell joins array elements by space char while convert the array to string. So, the regexp contains spaces (In this example, [^l o]). Spaces do not affect the result because the input string contains letters only.

\$\endgroup\$
1
\$\begingroup\$

APL (Dyalog Extended), 8 bytesSBCS

Anonymous tacit prefix function.

∊⊢⊆⍨1<⍧⍨

Try it online!

⍧⍨ count-in selfie (count occurrences of argument elements in the argument itself)

1< Boolean mask where one is less than that

⊢⊆⍨ partition the argument by that mask (beginning a new partition on 1s and removing on 0s)

ϵnlist (flatten)

\$\endgroup\$
1
\$\begingroup\$

JavaScript, 45 bytes

s=>[...s].filter(c=>s.match(c+'.*'+c)).join``
\$\endgroup\$
1
\$\begingroup\$

R, 70 bytes

a=utf8ToInt(scan(,''));intToUtf8(a[!a%in%names(table(a)[table(a)<2])])

Try it online!

A poor attempt, even from a TIOBE top 20 language. I know something can be done about the second half, but at the moment, any golfs escape me.

\$\endgroup\$
1
\$\begingroup\$

JavaScript, 34 bytes

Input as a string, output as a character array.

s=>[...s].filter(x=>s.split(x)[2])

Try It Online!

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 82 bytes

p=>[...p].map((v,i,a)=>a.filter(f=>f==v).length).reduce((a,c,i)=>c>1?a+=p[i]:a,[])

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ You can use .join`` instead of .join(""). \$\endgroup\$ – recursive Feb 25 at 21:35
0
\$\begingroup\$

PHP - 137

Code

implode('',array_intersect(str_split($text),array_flip(array_filter(array_count_values(str_split($text)),function($x){return $x>=2;}))));

Normal Code

$text   = 'helloworld';
$filter = array_filter(array_count_values(str_split($text)), function($x){return $x>=2;});
$output = implode('',array_intersect(str_split($text),array_flip($filter)));

echo $output;
\$\endgroup\$
0
\$\begingroup\$

PHP - 83 78

<?for($a=$argv[1];$i<strlen($a);$r[$a[$i++]]++)foreach($ras$k=>$c)if($c>1)echo$k

Improved version:

<?for($s=$argv[1];$x<strlen($s);$c=$s[$x++]) echo substr_count($s,$c)>1?$c:'';

Of course this needs notices to be turned off

Edit: Improvement inspired by @hengky mulyono

I am so bad at codegolf :)

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

C++, 139 bytes

string s;cin>>s;string w{s}; auto l=remove_if(begin(s),end(s),[&w](auto&s){return count(begin(w),end(w),s)==1;});s.erase(l,end(s));cout<<s;

ungolfed:

#include <algorithm>
#include <string>
#include <iostream>

int main() {
  using namespace std;
  string s;
  cin >> s;
  const string w{s};
  auto l = remove_if(begin(s), end(s), [&w](auto& s) {
                                         return count(begin(w), end(w), s) == 1;
                                       });
  s.erase(l, end(s));
  cout << s;
  return 0;
}
\$\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.