• A string containing printable ASCII (excluding spaces, tabs and new-lines)
  • A boolean


The parts of the String are divided into four groups:

  • Lowercase letters
  • Uppercase letters
  • Digits
  • Other

Based on the boolean, we either output the highest occurrence of one (or multiple) of these four groups, or the lowest, replacing everything else with spaces.

For example:

Input: "Just_A_Test!"
It contains:
- 3 uppercase letters: JAT
- 6 lowercase letters: ustest
- 0 digits
- 3 other: __!

These would be the outputs for true or false:

true:   " ust    est "

// digits have the lowest occurrence (none), so everything is replaced with a space
false:  "            "

(Note: You are allowed to ignore trailing spaces, so the outputs can also be " ust est" and "" respectively.)

Challenge rules:

  • The input will never be empty or contain spaces, and will only consist of printable ASCII in the range 33-126 or '!' through '~'.
  • You are allowed to take the input and/or outputs as character-array or list if you want to.
  • Any two consistent and distinct values for the boolean are allowed: true/false; 1/0; 'H'/'L'; "highest"/"lowest"; etc. Note that these distinct values should be used (somewhat) as a boolean! So it's not allowed to input two complete programs, one that gives the correct result for true and the other for false, and then having your actual code only be <run input with parameter>. Relevant new default loophole I've added, although it can still use a lot of finetuning regarding the definitions..
  • If the occurrence of two or more groups is the same, we output all those occurrences.
  • The necessary trailing spaces are optional, and a single trailing new-line is optional as well. Necessary leading spaces are mandatory. And any other leading spaces or new-lines aren't allowed.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code.
  • Also, please add an explanation if necessary.

Test cases:

Inputs:                              Output:

"Just_A_Test!", true                 " ust    est "     (or " ust    est")
"Just_A_Test!", false                "            "     (or "")
"Aa1!Bb2@Cc3#Dd4$", either           "Aa1!Bb2@Cc3#Dd4$"
"H@$h!n9_!$_fun?", true              " @$ !  _!$_   ?"
"H@$h!n9_!$_fun?", false             "H     9        "  (or "H     9")
"A", true                            "A"
"A", false                           " "                (or "")
"H.ngm.n", true                      "  ngm n"
"H.ngm.n", false                     "       "          (or "")
"H.ngm4n", false                     "H.   4 "          (or "H.   4")
  • \$\begingroup\$ Is it acceptable to output the most/fewest as separate entries? For example, for the "hashing is fun" test case, can "H " and " 9 " (with appropriate spaces) be output instead of "H 9"? \$\endgroup\$ – AdmBorkBork Oct 31 '17 at 14:53
  • \$\begingroup\$ @AdmBorkBork I don't get what you mean; both the H and 9 are part of the "fewest". \$\endgroup\$ – Erik the Outgolfer Oct 31 '17 at 15:08
  • \$\begingroup\$ Can the boolean input value be "max"/"min", which is then used as Math[b] to refer to Math.max or Math.min? \$\endgroup\$ – Justin Mariner Nov 1 '17 at 1:21
  • \$\begingroup\$ @JustinMariner You know.. I changed my mind sorry about that. I guess it's for JS? I think a lot of programming languages can utilize something like this, so too many of the existing answers should be changed. So sorry, you'll have to keep the b?"max":"min" in your answer.. It's a fine line I guess, maybe I should just use a truthy/falsey value next time.. \$\endgroup\$ – Kevin Cruijssen Nov 1 '17 at 8:07

16 Answers 16


Husk, 27 26 24 22 bytes

-2 bytes thanks to Zgarb

-2 bytes thanks to Leo

Takes ' ' as False and 'a' as True (In Husk, whitespace in Fasly and all other characters are Truthy)


Try it online!

How does it work?

Fż▲→ġ#¬Ö#≡⁰Ṫḟë½D±o¬□m;   Function, takes a character c and a string S as arguments
                    m;   Wrap each character in S into it's own string
             ë           List of four functions returning booleans:
              ½D±o¬      Lower case?,Upper case?,Digit?,Not alphanumeric?
           Ṫḟ            Outer product with find†
       Ö#≡⁰              Sort on how many characters have the same Truthyness as c
    ġ#¬                  Group strings with equal numbers of spaces
   →                     Take the last group
Fż▲                      Squash it all into one list

is a function that takes a predicate p and a list L and returns the first element of L that satisfies p. If no element satisfies p a default argument is returned. In this case ' '. By applying to a one character string, we are essentially saying if p c then c else ' '.

Is function that takes a function f and two lists L1,L2. It returns a table of f applied over all pairs of L1 and L2. In this case f is , L1 is our list of 4 functions, and L2 is the list of one character strings.

After Ṫḟ we have a list of strings where each string is the result of replacing characters which don't satisfy one of the rules with a ' '.

NB: In newer versions of Husk, ġ#¬Ö#≡⁰ can be replaced by k#≡⁰ for a 3 byte saving!

| improve this answer | |
  • \$\begingroup\$ Out of curiosity: why ' ' and 'a'? Maybe I understand it better when the explanation is added, because I can't read Husk. ;) \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 20:05
  • \$\begingroup\$ Nice! Here's 24 bytes using . \$\endgroup\$ – Zgarb Oct 31 '17 at 21:56
  • \$\begingroup\$ @Zgarb Thanks! I didn't really understand what Mmm was doing myself :) \$\endgroup\$ – H.PWiz Oct 31 '17 at 22:00
  • \$\begingroup\$ Unfortunately this doesn't really help with golfing, but S`?' might be simpler as ?IK' \$\endgroup\$ – Leo Oct 31 '17 at 22:59
  • \$\begingroup\$ I tend to avoid using I, sometimes it makes the interpreter take forever. It also seems wasteful. \$\endgroup\$ – H.PWiz Oct 31 '17 at 23:05

Jelly, 31 bytes


Try it online!

The boolean values are 2 and 1 (or any other positive even/odd pair), which represent True and False respectively. I will try to add an explanation after further golfing.

Thanks to caird coinheringaahing for saving 2 bytes, and to Lynn for saving 4 bytes! Thanks to one of Erik's tricks, which inspired me to save 4 bytes!

How it works

Note that this is the explanation for the 35-byte version. The new one does roughly the same (but tweaked a bit by Lynn), so I won't change it.

ØBUs26f€³µ³ḟØBW,µẎLİ⁴¡$ÐṀF - Niladic helper link.
ØB                         - String of base digits: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
  U                        - Reverse.
   s26                     - Chop into sublists of length 26, preserving shorter
                             trailing substrings.
      f€³                  - For each, keep the common characters with the input.
            ØB             - Base digits.
          ³ḟ               - Get the signs in the input. Filter the characters of the
                             input that aren't alphanumeric.
              W,µẎ         - Concatenate (wrap, two element list, tighten).
                       ÐṀ  - Keep the elements with maximal link value.
                  L        - Length.
                    ⁴¡     - Do N times, where N is the second input.
                   İ       - Inverse. Computes 1 ÷ Length. 2 maps to the length itself,
                             because 1 ÷ (1 ÷ Length) = length; 1 yields
                             (1 ÷ Length), swapping the maximal numbers with minimal ones.
                         F - Flatten.

¹⁶e¢$?€ - Main link.
      € - For each character.
   e¢?  - If it is contained by the last link (called niladically), then:
¹       - Identity, the character itself, else:
 ⁶      - A space.
| improve this answer | |
  • \$\begingroup\$ 40 bytes \$\endgroup\$ – caird coinheringaahing Oct 31 '17 at 13:40
  • \$\begingroup\$ @cairdcoinheringaahing Thanks! :) I wanted to golf that part ever since I posted the answer, but I couldn't figure out why it didn't work... I had an extraneous µ D: \$\endgroup\$ – Mr. Xcoder Oct 31 '17 at 13:42
  • \$\begingroup\$ 31 bytes: generate the classes as ØṖḟØBṭØBUs26¤, and then test membership with f and Ç instead of e¢$. \$\endgroup\$ – Lynn Oct 31 '17 at 16:55

Python 2, 166 158 bytes

t=lambda c:('@'<c<'[','`'<c<'{','/'<c<':',1-c.isalnum())
def f(s,b):x=map(sum,zip(*map(t,s)));print''.join([' ',c][x[t(c).index(1)]==sorted(x)[-b]]for c in s)

Try it online!

| improve this answer | |
  • \$\begingroup\$ 158 bytes \$\endgroup\$ – Mr. Xcoder Oct 31 '17 at 13:27
  • \$\begingroup\$ @Mr.Xcoder Thanks; Just got that as well :) \$\endgroup\$ – TFeld Oct 31 '17 at 13:28

R, 193 186 179 158 bytes

-7 bytes thanks to NofP and his suggestion of cbind

-6 bytes using outer, -1 byte switching [^a-zA-Z0-9] with [[:punct:]]

-21 bytes thanks to MickyT for pointing out a list of characters is allowed

S[!colSums(y[(s=rowSums(y))=="if"(B,max,min)(s),,drop=F])]=" "

Verify all test cases

Takes 1/T as truthy (max) and 0/F as falsey (min), and takes S as a list of single characters.

Try it online!

In my original version (with NofP's suggestions), the matrix y is constructed by evaluating grepl(regex, S) for each regex, then concatenating them together as columns of a matrix. This results in multiple calls to grepl, but as S is fixed, it seemed that something else needed to be done. As I noted:

There are potentially shorter approaches; mapply, for example:


unfortunately, this will not simplify as a matrix in the 1-character example of "A".

I used outer rather than mapply, which always returns an array (a matrix in this case), and was forced to Vectorize grepl, which is really just an mapply wrapper around it.

I also discovered the predefined character group [:punct:] which matches punctuation (non-space, non-alphanumeric) characters.

| improve this answer | |
  • 1
    \$\begingroup\$ If you replace the matrix with a cbind, you could reduce to 186 bytes: y=cbind(g("[a-z]",S),g("[A-Z]",S),g("\\d",S),g("[^a-zA-Z0-9]",S)) \$\endgroup\$ – NofP Oct 31 '17 at 15:16
  • \$\begingroup\$ @NofP oh, very nice. Also, you can surround code with backticks (`) to have it display like this. :) \$\endgroup\$ – Giuseppe Oct 31 '17 at 15:22
  • \$\begingroup\$ The rules state that you can use a character array or list as input , so you could probably remove the S=el(strsplit(G,"")) \$\endgroup\$ – MickyT Oct 31 '17 at 22:10
  • \$\begingroup\$ @MickyT ah, I overlooked that, thank you. \$\endgroup\$ – Giuseppe Nov 1 '17 at 13:15

Husk, 31 29 28 bytes

SMS?' `ṁ§foSM≠?▲▼⁰M#Kë½D±o¬□

Uses 0 for minimal and 1 for maximal character counts. Try it online!


Lists of functions are cool.

SMS?' `ṁ§foSM≠?▲▼⁰M#Kë½D±o¬□  Inputs are bit B and string S.
                     ë        Make a list L of the four functions
                      ½       is-lowercase-letter,
                       D      is-uppercase-letter,
                        ±     is-digit, and
                         o¬□  not is-alphanumeric.
                  M#          For each of them, take number of matches in S,
              ?▲▼⁰            take maximum or minimum depending on B,
          oSM≠                and mark those entries that are not equal to it.
        §f          K         Remove from L the functions that correspond to marked entries, call the result L2.
                              These functions test whether a character should be replaced by a space.
SM                            Do this for each character C in S:
      `ṁ                      Apply each function in L2 to C and sum the results.
  S?'                         If the result is positive, return space, otherwise return C.
| improve this answer | |

Python 2, 140 bytes

g=lambda x:x.isalnum()-~(x>'Z')*x.isalpha()
def f(s,m):k=map(g,s).count;print''.join([' ',c][k(g(c))==sorted(map(k,range(4)))[m]]for c in s)

Try it online!

Jonathan Frech saved a byte. Thanks!

Highest is m=-1, lowest is m=0.

| improve this answer | |
  • 1
    \$\begingroup\$ I think you can save a byte by replacing +x.isalpha()*-~(x>'Z') with -~(x>'Z')*x.isalpha(). \$\endgroup\$ – Jonathan Frech Nov 1 '17 at 17:30

Jelly, 35 bytes


Try it online!

| improve this answer | |

Java (OpenJDK 8), 448 439 432 362 361 354 352 348 343 320 bytes

s->b->{int w[]=new int[4],m=0,n=-1>>>1,l;s.chars().forEach(c->{if(c>96&c<123)w[0]++;else if(c>64&c<91)w[1]++;else if(c>47&c<58)w[2]++;else++w[3];});for(int W:w){m=W>m?W:m;n=W<n?W:n;}l=m-n;m=b?m:n;return l<1?s:s.replaceAll("["+(w[0]!=m?"a-z":"")+(w[1]!=m?"A-Z":"")+(w[2]!=m?"\\d]":"]")+(w[3]!=m?"|[^a-zA-Z0-9]":"")," ");}

Try it online!

| improve this answer | |
  • \$\begingroup\$ 366 bytes \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 13:56
  • \$\begingroup\$ You can remove the + in \\|+$ for an additional -1 byte. \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 14:05
  • \$\begingroup\$ You can save three more bytes by changing the last part to String r=(w[0]!=m?"[a-z]|":"")+(w[1]!=m?"[A-Z]|":"")+(w[2]!=m?"[0-9]|":"")+(w[3]!=m?"[^a-zA-Z0-9]|":"");return r.isEmpty()?s:s.replaceAll(r.replaceAll(".$","")," ");}. \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 14:17
  • \$\begingroup\$ Oh, and n=s.length() can be n=-1>>>1 for an additional -4. \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 14:22
  • \$\begingroup\$ Oh, one more small thing to golf: [0-9] -> \\d \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 15:23

Ruby, 118 116 bytes

Takes 0 (lowest) or -1 (highest) for its second argument.

-2 bytes thanks to Lynn.

->s,t{s.gsub(/./){|c|[/\d/,/[a-z]/,/[A-Z]/,/[^\da-z]/i].group_by{|x|s.scan(x).size}.sort[t][1].any?{|x|x=~c}?c:" "}}

Try it online!


  s.gsub(/./) {|c|
    [ /\d/,
    .group_by {|x| s.scan(x).size }
    .any? {|x| x =~ c } ? c : " "
| improve this answer | |
  • \$\begingroup\$ Very cool answer! You can use -1 as the “highest” value and replace minmax[t] by sort[t]. \$\endgroup\$ – Lynn Nov 1 '17 at 17:10

Python 2, 190 183 174 173 bytes

Thanks to Jonathan Frech for shortening it

from re import*
def f(i,c):
 q='[a-z]','[A-Z]','\d','[\W_]';l=[len(set(finditer(p,i)))for p in q]
 for j,k in enumerate(l):
	if k-eval(c):i=compile(q[j]).sub(' ',i)
 print i

This takes the strings 'max(l)' and 'min(l)' as true and false. (I don't think this breaks the rules...?) This is longer than the other two python answers but different so I thought I'd post it. I'm not a great golfer so I'm guessing this could be improved further but all the things I tried didn't work.

Try it online!

| improve this answer | |
  • \$\begingroup\$ Hello, and welcome to the site! When I try to run this, I get errors, and I'm not entirely sure why. One reason could be that sum(1for m... should be sum(1 for m..., but I think there are other problems too. Could you provide a link to an online interpreter (such as tio) to demonstrate how you're calling this, and to show it isn't erroring? \$\endgroup\$ – James Nov 1 '17 at 17:06
  • \$\begingroup\$ @DJMcMayhem I just added a link, thanks for providing the link I wasn't sure how to do it. I am not getting an error when I run it there. \$\endgroup\$ – dylnan Nov 1 '17 at 17:13
  • \$\begingroup\$ Ah, I couldn't tell that you were inputting max(l) and min(l) as strings, that's why I was getting errors. Thanks for clearing that up! Although now, this is on the edge of violating rule #3, ` Note that these distinct values should be used (somewhat) as a boolean`, but it's definitely a little bit of a gray area. \$\endgroup\$ – James Nov 1 '17 at 17:17
  • \$\begingroup\$ BTW, here's a TIO pro-tip: If you put your function calls in the footer field, they won't be counted towards your byte count, so you can easily see how long your answer is: Try it online! \$\endgroup\$ – James Nov 1 '17 at 17:19
  • \$\begingroup\$ @DJMcMayhem Ah thanks. I agree it's kind of a gray area. I could take 'max' and 'min' as true false then do eval(c+'(l)') which adds 6 Bytes and seems more acceptable but until OP disallows my answer I'm assuming it's okay. \$\endgroup\$ – dylnan Nov 1 '17 at 17:21

JavaScript (ES6), 151 149 bytes

(s,f,a=[/\d/,/[A-Z]/,/[a-z]/,/[_\W]/],b=a.map(r=>s.split(r).length))=>s.replace(/./g,c=>b[a.findIndex(r=>r.test(c))]-Math[f?"max":"min"](...b)?' ':c)
<input id=s oninput=o.textContent=g(s.value,f.checked)><input id=f type=checkbox onclick=o.textContent=g(s.value,f.checked)><pre id=o>

Sadly the rules probably don't allow me to pass Math.max or Math.min as the flag. Edit: Saved 2 bytes thanks to @JustinMariner.

| improve this answer | |

Jelly, 37 bytes


Try it online!

-6 bytes "borrowing" from Erik's post :D

| improve this answer | |
  • \$\begingroup\$ Lol preserving the spaces is essentially half the program :D \$\endgroup\$ – Mr. Xcoder Oct 31 '17 at 13:21
  • \$\begingroup\$ Could you add an explanation, or are you still working on golfing it first? \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 13:21
  • \$\begingroup\$ @KevinCruijssen Golfing first :D \$\endgroup\$ – HyperNeutrino Oct 31 '17 at 13:38

Java (OpenJDK 8), 307 + 34 306 + 27 295 bytes

My "interesting" take on the challenge.

Thanks to Kevin Cruijssen for cutting down the import bytes removing the import entirely!

s->b->{String t=s.replaceAll("\\d","2").replaceAll("[a-z]","0").replaceAll("[A-Z]","1").replaceAll("\\D","3"),v="";int a[]={0,0,0,0},i=0,z=0,y=-1>>>1;t.chars().forEach(j->{a[j%4]++;});for(int x:a){z=x>z?x:z;y=x<y?x:y;}for(;i<s.length();i++)v+=a[t.charAt(i)%4]!=(b?z:y)?" ":s.charAt(i);return v;}

Try it online!


String t=s.replaceAll("\\d","2")

First replaces each group with an integer between 0 and 3 using some simple regex and stores this in a new String.

int a[]={0,0,0,0},m,i=0,z=0,y=-1>>>1;

Initialises an array of integers as well as a couple of other integers to use later. Sets the y variable to the max int size using unsigned right bit shift.


For each character in the modified string, this uses its ASCII value modulo 4 to calculate the index of the aforementioned array to increment.

for(int x:a){

This then loops through the counts of each group stored in the array and calculates the minimum (y) and the maximum (z).

    v+=a[t.charAt(i)%4]!=(b?z:y)?" ":s.charAt(i);

Loops through every character in the String again, checking if the group of that characters group is equal to the min/max (using the modulo trick mentioned earlier). If it isn't equal, then a space is added to the new String in the characters place, otherwise the original character is added.

return v;

Finally return the new String!

| improve this answer | |

Bash, 229 227 212 bytes

LANG=C;g="A-Z a-z 0-9 !A-Za-z0-9";declare -A H
f()(((i=$2?99:-1));r=$1;for h in $g;{ t=${r//[$h]};t=${#t};(($2?t<i:t>i))&&i=$t&&H=([$h]=1);((t-i))||H[$h]=1;};for h in $g;{((${H[$h]}))||r=${r//[$h]/ };};echo "$r")

Try it Online

| improve this answer | |
  • \$\begingroup\$ I'm not sure how the spaces around brackets and square-blocks work in Bash, but it still seems to work without the space at f(){((. \$\endgroup\$ – Kevin Cruijssen Oct 31 '17 at 16:11
  • 1
    \$\begingroup\$ yes, space is mandatory generally except for (, also 2 bytes could be saved using ( instead of {, degrading performance because creating a subshell \$\endgroup\$ – Nahuel Fouilleul Oct 31 '17 at 17:44

PHP, 161 158 bytes

for([,$s,$z]=$argv;~$c=$s[$i++];)foreach([punct,upper,lower,digit]as$f)(ctype_.$f)($c)?$$i=$f:$g[$f]++;while(~$c=$s[$k++])echo$g[$$k]-($z?min:max)($g)?" ":$c;

Run with -nr or try it online.

  • first loop: for each position, remember the group of the character
    and count the occurences of groups that the current character is not in.
    (that negation saved 3 bytes)
  • depending on second parameter, pick min non-count for truthy, max non-count for falsy.
  • second loop: if (group of current character) non-count differs
    from min/max non-count then print space, else print character.
| improve this answer | |
  • 1
    \$\begingroup\$ @KevinCruijssen Make sure you have the latest PHP version (7.1.0) selected. \$\endgroup\$ – Titus Nov 1 '17 at 2:05

JavaScript (ES6), 139 bytes

s=>b=>s.map(c=>++a[g(c)]&&c,a=[0,0,0,0],g=c=>c>-1?0:/[a-z]/i.test(c)?c<"a"?2:1:3).map(c=>a.map(v=>v-Math[b?"max":"min"](...a))[g(c)]?" ":c)

Input and output is an array of characters. Takes actual boolean values for input.

A different approach from @Neil's answer; almost avoiding regular expressions. Instead, I used a series of checks to determine the category of each character:

  • Digits return true for c>-1 because non-digits fail mathematical comparisons
  • Uppercase letters match the regex /[a-z]/i and have codepoints less than "a"
  • Lowercase letters match that regex but have codepoints not less than "a"
  • Symbols pass none of those tests

Test Cases

s=>b=>s.map(c=>++a[g(c)]&&c,a=[0,0,0,0],g=c=>c>-1?0:/[a-z]/i.test(c)?c<"a"?2:1:3).map(c=>a.map(v=>v-Math[b?"max":"min"](...a))[g(c)]?" ":c)

;[["Just_A_Test!", true],["Just_A_Test!", false],["Aa1!Bb2@Cc3#Dd4$", true],["Aa1!Bb2@Cc3#Dd4$", false],["H@$h!n9_!$_fun?", true],["H@$h!n9_!$_fun?", false],["A", true],["A", false],["H.ngm.n", true],["H.ngm.n", false],["H.ngm4n", false]]
.forEach(([S,B])=>console.log(`"${S}", ${B} -> "${f(S.split``)(B).join``}"`))

| improve this answer | |

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.