Detriplicate a string

39

1

A lot of languages have built-in ways to get rid of duplicates, or "deduplicate" or "uniquify" a list or string. A less common task is to "detriplicate" a string. That is, for every character that appears, the first two occurrences are kept.

Here is an example where the characters that should be deleted are labelled with ^:

aaabcbccdbabdcd
  ^    ^ ^^^ ^^
aabcbcdd

Your task is to implement exactly this operation.

Rules

Input is a single, possibly empty, string. You may assume that it only contains lowercase letters in the ASCII range.

Output should be a single string with all characters removed which have already appeared at least twice in the string (so the left-most two occurrences are kept).

Instead of strings you may work with lists of characters (or singleton strings), but the format has to be consistent between input and output.

You may write a program or a function and use any of the our standard methods of receiving input and providing output.

You may use any programming language, but note that these loopholes are forbidden by default.

This is , so the shortest valid answer – measured in bytes – wins.

Test Cases

Every pair of lines is one test case, input followed by output.



xxxxx
xx
abcabc
abcabc
abcdabcaba
abcdabc
abacbadcba
abacbdc
aaabcbccdbabdcd
aabcbcdd

Leaderboard

The Stack Snippet at the bottom of this post generates a leaderboard from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 3 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 86503; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 8478; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "https://api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang.toLowerCase(), user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

Martin Ender

Posted 2016-07-25T07:50:48.767

Reputation: 184 808

5Singleton strings... stringletons? – dkudriavtsev – 2016-07-28T19:00:01.103

Answers

23

Jelly, 6 bytes

Qx2œ&@

Try it online! or verify all test cases.

How it works

Qx2œ&@  Main link. Argument: s (string)

Q       Unique; deduplicate s.
 x2     Repeat each character.
   œ&@  Take the multiset intersection of s and the previous result.

Dennis

Posted 2016-07-25T07:50:48.767

Reputation: 196 637

15

JavaScript (ES6), 42 48

Edit A whopping 6 bytes saved thx @Neil

s=>s.replace(k=/./g,c=>(k[c]+=c)[11]?'':c)

Explanation: I use the properties 'a'...'z' of object k to store info for each character (object k is a regexp in this case just to save bytes). These properties are initially undefined. In javascript adding a number to undefined gives NaN (quite sensible), but adding a string 'X' gives "undefinedX" - a string of length 10 (silly). Adding more characters you get longer strings. If the obtained string for a given character is longer than 11, that character is not copied to output.

Test

F=
s=>s.replace(k=/./g,c=>(k[c]+=c)[11]?'':c)

test=`

xxxxx
xx
abcabc
abcabc
abcdabcaba
abcdabc
abacbadcba
abacbdc
aaabcbccdbabdcd
aabcbcdd`.split`\n`
for(i=0;i<test.length;)
  a=test[i++],b=test[i++],r=F(a),
  console.log(r==b?'OK':'KO',a,'->',r,b)

edc65

Posted 2016-07-25T07:50:48.767

Reputation: 31 086

Strictly speaking an empty line is one of the test cases. – Neil – 2016-07-26T12:28:23.147

@Neil ok added the empty string test – edc65 – 2016-07-26T14:40:33.573

If you switch to array input and output, you can use .filter to save 12 more characters. v=>v.filter(x=>!(v[x]+=x)[11]). Kudos on the "undefined" hack. – Grax32 – 2017-01-31T18:57:30.803

@Grax thanx but too different. Should post it yourself – edc65 – 2017-01-31T20:32:36.730

14

Python 2, 48 bytes

lambda s:reduce(lambda r,c:r+c*(r.count(c)<2),s)

c[r.count(c)/2:] is a same-length alternative to c*(r.count(c)<2) .


49 bytes:

r=''
for c in input():r+=c*(r.count(c)<2)
print r

xnor

Posted 2016-07-25T07:50:48.767

Reputation: 115 687

12

Retina, 17 bytes

(.)(?<=\1.*\1.+)

Try it online!

Simple regex replace - match a character if it already appeared twice, and remove it.

Kobi

Posted 2016-07-25T07:50:48.767

Reputation: 728

I also tried a loop, and a repeated group with {2}, both with 18 bytes.

– Kobi – 2016-07-25T10:54:09.507

1I've got 14 using a recently added feature. ;) – Martin Ender – 2016-07-25T10:56:58.623

I knew there was something. I looked at limits, probably not that one. I'll check again. – Kobi – 2016-07-25T11:02:25.830

3

Ah, I think I found Martin's answer. I had some trouble when I was trying before, I think because I didn't consider how deduplicate would work on a multi-line input. Spoiler (with 5 bytes added to enable per-line mode): http://retina.tryitonline.net/#code=JShHYApEYCguKSg_PD1cMS4rKQ&input=eHh4eHgKeHgKYWJjCmFiYwphYmNhYmMKYWJjYWJjCmFiY2RhYmNhYmEKYWJjZGFiYwphYmFjYmFkY2JhCmFiYWNiZGMKYWFhYmNiY2NkYmFiZGNkCmFhYmNiY2Rk

– FryAmTheEggman – 2016-07-25T12:57:22.793

@FryAmTheEggman - Nice, I didn't find this one. Feel free to add an answer - I think this is too different from my answer and I didn't feel comfortable editing it in :P. Thanks! – Kobi – 2016-07-28T14:20:03.077

Alright, (I think they are actually very similar, but) I've posted another answer :P

– FryAmTheEggman – 2016-07-28T15:16:49.150

6

Brachylog, 25 bytes

.v|s.g:.z:1a
:2fl<3
he~t?

Try it online! or verify all test cases.

Explanation

This works because s - Subset will unify with bigger subsets first, thus e.g. for "aaa" it will try "aa" before "a".

  • Main predicate:

      .v         input = Output = ""
    |          Or
      s.         Output is an ordered subset of the input
      g:.z       Zip each character of the output with the output itself
      :1a        Apply predicate 1 on each element of the zip
    
  • Predicate 1: Check that all characters only appear at most twice. Input = [String:Char]

    :2f        Find all valid outputs of predicate 2 (i.e. one output per occurence
                   of the char)
    l<3        There are less than 3 occurences
    
  • Predicate 2: Get an occurence of a character. Input = [String:Char]

    he         Take a character of the string in the input
      ~t?      That character is the char of the input
    

Fatalize

Posted 2016-07-25T07:50:48.767

Reputation: 32 976

6

><>, 22 bytes

i:0(?;::9g:}2(?o{1+$9p

Try it online! Uses the codebox to keep track of counts so far.

i                       Read a char c of input
 :0(?;                  Halt if EOF
      :                 Make a copy - stack has [c c] at the top
       :9g              Get count stored at (c, 9)
          :}            Copy the count and move to bottom of stack
            2(?o        If the count is less than 2, output c
                {1+     Move the count back to the top of the stack and increment
                   $9p  Update cell at (c, 9)
                        [Instruction pointer moves to start as ><> is toroidal]

Sp3000

Posted 2016-07-25T07:50:48.767

Reputation: 58 729

6

J, 20 15 bytes

#~(3>[+/@:={:)\

This defines a monadic function that takes and returns a string. Try it here. Usage:

   f =: #~(3>[+/@:={:)\
   f 'abaacbb'
abacb

Explanation

I switched to the same algorithm that some other solutions use, since it turned out to be shorter...

#~(3>[+/@:={:)\  Input is y.
  (          )\  For each prefix of y:
          =        compute the equality vector
     [     {:      of the prefix and its last element, and
      +/@:         take its sum. Now we have a vector r such that y[i] has its
                   r[i]'th occurrence at position i.
   3>              Mark those coordinates where r[i] < 3.
#~               Remove the non-marked characters from y.

Zgarb

Posted 2016-07-25T07:50:48.767

Reputation: 39 083

6

Haskell, 40 39 bytes

foldl(\s c->s++[c|filter(==c)s<=[c]])""

Usage example: foldl(\s c->s++[c|filter(==c)s<=[c]])"" "aaabcbccdbabdcd" -> "aabcbcdd".

Keep the next char c if the string of all cs so far are lexicographical less or equal the singleton string [c].

Edit: @xnor saved a byte by switching from list comprehension to filter. Thanks!

nimi

Posted 2016-07-25T07:50:48.767

Reputation: 34 639

Your alternative could do filter(==c)s<=[c] to save a byte. – xnor – 2016-07-25T21:23:21.133

5

Perl, 22 bytes

21 bytes code + 1 for -p.

s/./$&x(2>${$&}++)/ge

Usage

perl -pe 's/./$&x(2>${$&}++)/ge' <<< 'aaabcbccdbabdcd'
aabcbcdd

Dom Hastings

Posted 2016-07-25T07:50:48.767

Reputation: 16 415

5

C, 57 bytes

Call f() with the string to detriplicate. The function modifies its parameter. Requires C99 because of the for-loop declaration.

f(char*p){for(char*s=p,m[256]={0};*s=*p;s+=++m[*p++]<3);}

owacoder

Posted 2016-07-25T07:50:48.767

Reputation: 1 556

Can't you put the declaration of s into the first statement of the for? – Martin Ender – 2016-07-25T14:00:21.437

In C99 you can. I just didn't because I like to keep golfs C89 compatible. – owacoder – 2016-07-25T14:39:53.897

5

JavaScript (ES6), 35 bytes

s=>s.filter(c=>(s[c]=(s[c]|0)+1)<3)

Takes an array of characters as input and returns the detriplicated array.

c.P.u1

Posted 2016-07-25T07:50:48.767

Reputation: 1 049

Nice. You could do c=>(s[c]=-~s[c])<3 to save a few bytes. – ETHproductions – 2017-01-18T17:13:12.643

I'd missed that you could use arrays as input and had written a function using map. Golfed it looked essentially like yours. the main difference was the assignment, which if you'll switch it around will save a few bytes. Try s.filter(c=>(s[c]=s[c]+1|0)<3) for 33 bytes. EDIT: Whoops, missed the comment above me, that's even better :) – Jan – 2017-02-07T18:34:07.530

4

MATL, 8 bytes

t&=Rs3<)

Try it online!

Explanation

t      % Input string implicitly. Push another copy
&=     % Matrix of all pairwise equality comparisons of string elements
R      % Keep only upper triangular part, making the rest of the entries zero
s      % Sum of each column. This gives a vector with number of occurrences
       % of the current character up to the current position
3<     % True for entries that are less than 3
)      % Use as logical index into initial copy of the input. Display implicitly

Example

Assuming input 'aaababbc', the stack contains the following after the indicated statements:

  • t

    'aaababbc'
    'aaababbc'
    
  • t&=

    'aaababbc'
    [ 1 1 1 0 1 0 0 0;
      1 1 1 0 1 0 0 0;
      1 1 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      1 1 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      0 0 0 1 0 1 1 0;
      0 0 0 0 0 0 0 1 ]
    
  • t&=R

    'aaababbc'
    [ 1 1 1 0 1 0 0 0;
      0 1 1 0 1 0 0 0;
      0 0 1 0 1 0 0 0;
      0 0 0 1 0 1 1 0;
      0 0 0 0 1 0 0 0;
      0 0 0 0 0 1 1 0;
      0 0 0 0 0 0 1 0;
      0 0 0 0 0 0 0 1 ]
    
  • t&=Rs

    'aaababbc'
    [ 1 2 3 1 4 2 3 1 ]
    
  • t&=Rs3<

    'aaababbc'
    [ true true false true false true false true ]
    
  • t&=Rs3<)

    'aabbc'
    

Luis Mendo

Posted 2016-07-25T07:50:48.767

Reputation: 87 464

4

PowerShell v2+, 31 bytes

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

Uses the same regex as in Kobi's Retina answer, just encapsulated in the PowerShell -replace operator. Works because both are using .NET-flavor regex in the background.

Alternatively, without regex, 56 bytes

$b=,0*200;-join([char[]]$args[0]|%{"$_"*($b[$_]++-lt2)})

Creates a helper array $b pre-populated with 0s. Casts the input string $args[0] as a char-array, pipes it through a loop |%{...}. Each iteration outputs the current character $_ as a string "$_" multiplied by a Boolean that is only $TRUE (implicitly cast to 1 here) if the appropriate point in the helper array is less than 2 (i.e., we haven't seen this char twice already). The resultant collection of strings is encapsulated in parens and -joined together to form a single output string. That's left on the pipeline and output is implicit.

AdmBorkBork

Posted 2016-07-25T07:50:48.767

Reputation: 41 581

regex is unbeatable. :) I beleave a hashtable is better then an array for the variant without regex: $b=@{};-join($args|% t*y|?{++$b.$_-lt3}). – mazzy – 2018-09-10T13:26:10.067

1@mazzy For the variant without regex and your code, it would need to be a newer version than PowerShell 2. As a result, I think I'll keep this answer without change. You can post your code as a separate answer, though! – AdmBorkBork – 2018-09-10T17:46:34.453

did the hashtable appear in the version 3.0? Ok. Thanks. – mazzy – 2018-09-10T17:50:59.070

4

Mathematica, 39 bytes

Fold[If[Count@##<2,Append@##,#]&,{},#]&

Anonymous function. Takes a character list as input, and returns the detriplicated list as output. Uses the method of folding over the list and rejecting triplicate elements, it's not too complicated.

LegionMammal978

Posted 2016-07-25T07:50:48.767

Reputation: 15 731

4

05AB1E, 12 bytes

vyˆ¯y¢O3‹iy?

Explanation

v            # for each char in input
 yˆ          # push to global array
   ¯y¢O3‹i   # if nr of occurrences are less than 3
          y? # print it

Try it online

Emigna

Posted 2016-07-25T07:50:48.767

Reputation: 50 798

4

Retina, 14 bytes

D`(.)(?<=\1.*)

Verify all test cases. (The % enables per-line mode)

Uses the new "Deduplicate" stage to save a couple bytes over Kobi's approach. Deduplicate gathers a list of all matches to the regex and replaces all but the first with the empty string. The regex matches a character that already appears once in the string, meaning that the first two will be kept.

FryAmTheEggman

Posted 2016-07-25T07:50:48.767

Reputation: 16 206

3

Pyke, 16 bytes

F~kR/2<Ii?+k(K~k

Try it here!

Blue

Posted 2016-07-25T07:50:48.767

Reputation: 26 661

3

K, 18 Bytes

  g:{x{?x@<x}@,/2#'=x}
  g "abc"
"abc"
  g "aaabcbccdbabdcd"
"aabcbcdd"

  /k4 request test vectors from internet
  R:"GET /raw/ftHe0bpE HTTP/1.0\r\nHost: pastebin.com\r\n\r\n"
  t:+0N 2#t@1_&|\(0=#:)'t:1_"\r\n"\:`:http://pastebin.com:80 R 

  /k4 no internet? use a file called "t.txt" in current directory
  t:+0N 2#0:`:t.txt

  /k6?
  t:+0N 2#0:"t.txt"

  /visually inspect test cases
  g't[0]
(();"xx";"abcabc";"abcdabc";"abacbdc";"aabcbcdd")

  /do all tests pass?
  |/ t[1] {$[0=#x;0=#y;x~y]}' g't[0]
1b

K4 is available for free download; K6 is in development. If you've downloaded KDB, you can get into K with backslash.

It may be easiest to see this broken apart, but first some syntax: g:x sets g to x. {x+1} is a function which takes an argument x. In K the first argument to a function is x (the second is y and the third is z. Don't need a fourth).

Now:

x:"aaabcbccdbabdcd"

=x means group x, which produces:

"abcd"!(0 1 2 10;3 5 9 11;4 6 7 13;8 12 14)

2#' means two taken(from) each which produces

"abcd"!(0 1;3 5;4 6;8 12)

As you can see, these are the offsets of the first two matches of each character. The 2 could be generalised.

,/ means join each and is often called raze. It's going to get us just the values of our dictionary. Thus, ,/"abcd"!(0 1;3 5;4 6;8 12) produces:

0 1 3 5 4 6 8 12

which we need to sort. {x@<x}@ is an idiom K programmers often see (Q calls it asc), which says x at grade-up x. Breaking it apart:

  <0 1 3 5 4 6 8 12
0 1 2 4 3 5 6 7

returned the indices of the sorted array, which we want taken from the original array. x@y means x at y so this indexes the array with the indices of the sort (if that makes any sense).

  {x@<x}@0 1 3 5 4 6 8 12
0 1 3 4 5 6 8 12

which we simply now index into our original array. We could say x@ here, but K supports a really powerful concept which we can take advantage of here: function application is indexing. That means that a[0] could be looking up the zeroth slot of a or it could be applying the 0 to the function called a. The reason we needed the @ previously in {x@<x} is because x<y means xs less than ys: Operators in K have a dyadic form (two-argument) and a monadic form (one-argument) that comes from APL. Q doesn't have this "ambivalence".

geocar

Posted 2016-07-25T07:50:48.767

Reputation: 214

Welcome to PPCG! Great first answer. :) – Martin Ender – 2016-07-31T08:06:51.827

I have a couple of questions. 1. Is K4 the same language as the one you link to (Q/kdb+)? 2. Could you show how to call your function on an input or how the items in testVectors.txt should be formatted? – Dennis – 2016-08-01T06:45:39.870

@Dennis 1. Yes. Press backslash to get from Q to K. 2. Just as they appear in the question: http://pastebin.com/ftHe0bpE example call: g"aaabcbccdbabdcd"

– geocar – 2016-08-01T09:23:29.007

OK, thanks. Couldn't get the file part working, but g"..." does the trick. Unfortunately, your code returns aabbcc for input abc. – Dennis – 2016-08-01T15:28:03.730

@Dennis You might have done something wrong: {x{?x@<x}@,/2#'=x}"abc" definitely returns "abc". It would return "aabbcc" if you missed the ? distinct. – geocar – 2016-08-01T15:43:32.897

\:testVectors.txtis a handle to the filetextVectors.txtand not a file called:testVectors.txt` NB the handle begins with the syntax backtick-colon. – geocar – 2016-08-01T15:44:55.090

I'm not sure how or why, but I executed the code from the revision history. The current version does indeed work. Apologies. I specified the correct filename (without a colon) but it didn't work. That doesn't matter though. A few suggestions: 1. We don't require functions to be named. {x{?x@<x}@,/2#'=x} is a valid submission. 2. You should set the actual submission apart from the test run so one can see clearly where the byte count comes from. 3. For K noobs like me, a test run that shows how to drop to K and doesn't involve a file would be helpful. – Dennis – 2016-08-01T15:52:52.497

@Dennis - 1. I wasn't counting the characters in the assignment. 2. I've expanded the introduction and would appreciate feedback on it; can you figure it out now? 3. I've added language about how to get into K4 by providing a link to the documentation. If you want help with K, the #kq IRC channel on freenode is usually helpful. – geocar – 2016-08-01T16:20:21.817

Let us continue this discussion in chat.

– geocar – 2016-08-01T16:28:44.423

2

SmileBASIC, 77 72 69 68 bytes

DIM R[#Y]READ S$WHILE""<S$Q=ASC(S$)INC R[Q]?SHIFT(S$)*(R[Q]<3);
WEND

Explained:

DIM R[128] 'array to store letter frequencies
READ S$ 'get input string
WHILE""<S$ 'much shorter than LEN(S$)
 Q=ASC(S$) 'get ascii value of first character in S$
 INC R[Q]
 ?SHIFT(S$)*(R[Q]<3); 'remove the first character of S$, and print it if there are less than 3 occurrences.
WEND

12Me21

Posted 2016-07-25T07:50:48.767

Reputation: 6 110

Welcome to ppcg! Nice first post! – Rɪᴋᴇʀ – 2017-01-24T05:09:36.123

2

Python 2, 51 bytes

f=lambda s:s and f(s[:-1])+s[-1]*(s.count(s[-1])<3)

Test it on Ideone.

Dennis

Posted 2016-07-25T07:50:48.767

Reputation: 196 637

2

Perl 6, 27 bytes

{.comb.grep({++%.{$_} <3})}

Explanation:

{.comb.grep({++%.{$_} <3})}
{                         } # a function
 .comb                      # get all the characters in the argument
      .grep({           })  # filter
               %.           # an anonymous hash (shared between calls to grep)
             ++  {$_}       # increment the value at the current key (current letter).
                            # if the key doesn't exist, it defaults to 0 (then gets incremented)
                      <3    # return True if it wasn't seen 3 times

(Note: Perl 6 isn't as "golf-oriented" as its sister Perl 5 ... So yes, that space before the < is necessary. The %.{} is an anonymous hash).

Ven

Posted 2016-07-25T07:50:48.767

Reputation: 3 382

24 bytes – Jo King – 2018-09-08T09:25:00.850

2

Java 8 lambda, 90 characters

i->{int[]o=new int[128];String r="";for(char c:i.toCharArray())if(++o[c]<3)r+=c;return r;}

Ungolfed version:

public class Q86503 {

    static String detriplicate(String input) {
        int[] occurences = new int[128];
        String result = "";
        for (char c : input.toCharArray()) {
            if (++occurences[c] < 3) {
                result += c;
            }
        }
        return result;
    }
}

Creates an array for all ascii characters. If a character occurs the corresponding counter will be increased. If it is over 2 the character won't be appended to the result string. Very easy, very short ;)

Frozn

Posted 2016-07-25T07:50:48.767

Reputation: 381

1

JavaScript, 30 bytes

v=>v.filter(x=>!(v[x]+=x)[11])

Using the method that @edc65 came up with for counting but with an array filter. First time character appears, the object value gets "undefined" plus the character (i.e. "undefinedx"). The next time the object value becomes "undefinedxx".

After that, v[x][11] returns true and when combined with the not operator, false, meaning characters that have already appeared twice will be filtered.

Grax32

Posted 2016-07-25T07:50:48.767

Reputation: 1 282

1

Common Lisp, 127

(lambda(s)(map()(lambda(x)(flet((p(b)(1+(position x s :start b))))(setf s(remove x s :start(p(p 0))))))(remove-duplicates s))s)

Pretty-printed

(lambda (s)
  (map nil
       (lambda (x)
         (flet ((p (b)
                  (1+ (position x s :start b))))
           (setf s (remove x s :start (p (p 0))))))
       (remove-duplicates s))
  s)

coredump

Posted 2016-07-25T07:50:48.767

Reputation: 6 292

1

Ruby, 79 62 57 bytes

This is pretty unwieldy, but I'm not sure I can golf this much better at the moment. Any golfing suggestions are welcome. Try it online!

Edit: -17 bytes thanks to Value Ink by suggesting a golfier way to remove triplicate characters. -5 bytes from removing the .uniq method.

->s{s.chars.map{|a|s[s.rindex a]=""while s.count(a)>2};s}

Ungolfed:

def g(s)
 s.chars.each do |a|
  while s.count(a) > 2
   i = s.rindex(a)
   s[i] = ""
  end
 end
 return s
end

Sherlock9

Posted 2016-07-25T07:50:48.767

Reputation: 11 664

62 bytes: ->s{s.chars.uniq.map{|a|s[s.rindex a]=""while s.count(a)>2};s} – Value Ink – 2017-01-24T05:03:17.887

1

K, 27 Bytes

    f:{x{x@<x}@,/{?2#&x}'x~'/:?x}
    testList:("xxxxx";"abcabc";"abcdabcaba";"abacbadcba";"aaabcbccdbabdcd")
    f'testList
("xx";"abcabc";"abcdabc";"abacbdc";"aabcbcdd")

Chromozorz

Posted 2016-07-25T07:50:48.767

Reputation: 338

1

Q, 52 Bytes

q)f2:{x asc raze{distinct 2#where x}each x~'/:distinct x}
q)f2 each testList
"xx"
"abcabc"
"abcdabc"
"abacbdc"
"aabcbcdd"
q)

Chromozorz

Posted 2016-07-25T07:50:48.767

Reputation: 338

0

Clojure, 72 bytes

#(apply str(reduce(fn[r c](if(<(count(filter #{c}r))2)(conj r c)r))[]%))

So many bytes...

NikoNyrh

Posted 2016-07-25T07:50:48.767

Reputation: 2 361

0

Pascal (FPC), 103 bytes

var a:array['a'..'z']of word;c:char;begin repeat read(c);inc(a[c]);if a[c]<3then write(c)until eof end.

Try it online!

Explanation:

var a:array['a'..'z']of word; //used for counting occurences of characters in the input
                              //array indices are accessed by chars
    c:char;
begin
  repeat
    read(c);                  //read a character from input
    inc(a[c]);                //increment the count of that character (its number in array)
    if a[c]<3 then write(c)   //if this is character's 1st or 2nd occurence, output it
  until eof                   //go back to reading if input is not read completely
end.

AlexRacer

Posted 2016-07-25T07:50:48.767

Reputation: 979

0

Kotlin, 50 bytes

{it.filterIndexed{i,c->it.take(i).count{it==c}<2}}

Try it online!

snail_

Posted 2016-07-25T07:50:48.767

Reputation: 1 982

0

sed (with -r), 25 bytes

:x;s/((.).*\2.*)\2/\1/;tx

Try it online!

Takes input from STDIN, outputs to STDOUT. Very straightforward implementation: does a regexp match for triple characters, deletes the last one, and loops until it didn't find anything else.

Sophia Lechner

Posted 2016-07-25T07:50:48.767

Reputation: 1 200

0

Bash + GNU sed, 18 bytes

sed -es/{a..z}//3g

Try it online!

Way better than my pure sed answer. I marked it as GNU sed because POSIX sed doesn't define the result of supplying both a number and g as flags to the s command. GNU sed, as an extension, takes this to mean "replace all matches starting with the third", which is just what we want.

Sophia Lechner

Posted 2016-07-25T07:50:48.767

Reputation: 1 200

0

05AB1E, 7 bytes

ʒIN£y¢!

Try it online!

Grimmy

Posted 2016-07-25T07:50:48.767

Reputation: 12 521

0

PHP, 42 bytes

Loop through the string, count each character in an array, only echo if the letter hasn't reached a count of 3 yet.

while($e=$argn[$x++])if($s[$e]++<2)echo$e;

Try it online!

XMark

Posted 2016-07-25T07:50:48.767

Reputation: 141

0

Javascript (using external library) (80 bytes)

This was a good one! Didn't win but it was fun

n=>{a={};return _.From(n).Where(x=>{b=a[x]?a[x]++:a[x]=1;return b<2}).Write("")}

Link to lib: https://github.com/mvegh1/Enumerable/

Code explanation: Method accepts a string, library parses it as a char array, and the Where clause is a complex filtering predicate that checks the 'a' hashmap for presence of the current char. If exists, increment counter, else set to 1. If < 2, the predicate (and current char) passes, else fail

enter image description here

applejacks01

Posted 2016-07-25T07:50:48.767

Reputation: 989

You can avoid using a return but making your function a comma-separated list of a expressions in parentheses: n=>(a={},_From(n)....). The last expression is the return value. In your Where function, you can eliminate the intermediate b entirely by comparing against the result of the assignment or increment: x=>(a[x]?a[x]++:a[x]=1)<2. – apsillers – 2016-07-25T16:33:23.027

Finally, you can avoid using an external library at all (and save bytes) using the string-split ellipsis and filter with join: [...n].filter(...).join(""). Flip the true/false logic when changing Where to filter. – apsillers – 2016-07-25T16:40:07.433

Ahh good observations! Ill take a closer look later at your suggestion – applejacks01 – 2016-07-25T17:53:30.073

-4

TCC, 5 4 bytes

$~;2

Try it online!

     | Print (implicit)
$~   | Limit char occurence
     | of input (assumed if not given)
  ;  | seperator
   2 | to 2

brianush1

Posted 2016-07-25T07:50:48.767

Reputation: 300

2Do you have a link to the language specification as well? – Martin Ender – 2016-07-25T11:21:29.440

Nope. I just know Lua, so I learned the language myself, but I can't find a specification anywhere. You can find the Lua source at https://www.ccode.gq/projects/tcc.lua

– brianush1 – 2016-07-25T11:29:45.703

4Did this work with a version of tcc.lua before the challenge was posted? Since the $~ command was modified at 16-07-25 12:57 UTC and you've recently added commands to solve three other challenges, I assume it didn't. If your answer requires a version of the language that postdates the challenge, you must label it as non-competing in the header. I'll remove my downvote when you add the label or provide proof that your code worked in an earlier version. – Dennis – 2016-07-26T17:07:54.367