# Highest or Lowest Occurrences?

## Challenge:

Inputs:

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

Output:

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.

## 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")

• 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"? – AdmBorkBork Oct 31 '17 at 14:53
• @AdmBorkBork I don't get what you mean; both the H and 9 are part of the "fewest". – Erik the Outgolfer Oct 31 '17 at 15:08
• Can the boolean input value be "max"/"min", which is then used as Math[b] to refer to Math.max or Math.min? – Justin Mariner Nov 1 '17 at 1:21
• @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.. – Kevin Cruijssen Nov 1 '17 at 8:07

# Husk, 272624 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)

Fż▲→ġ#¬Ö#≡⁰Ṫḟë½D±o¬□m;


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!

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

# Jelly, 31 bytes

ØṖḟØBṭØBUs26¤f€³Lİ⁴¡$ÐṀFf ¹⁶Ç?€  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
abcdefghijklmnopqrstuvwxyz'.
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.  • 40 bytes – caird coinheringaahing Oct 31 '17 at 13:40 • @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: – Mr. Xcoder Oct 31 '17 at 13:42 • 31 bytes: generate the classes as ØṖḟØBṭØBUs26¤, and then test membership with f and Ç instead of e¢$. – 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!

• 158 bytes – Mr. Xcoder Oct 31 '17 at 13:27
• @Mr.Xcoder Thanks; Just got that as well :) – TFeld Oct 31 '17 at 13:28

# R, 193186179 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

function(S,B){y=outer(c("[a-z]","[A-Z]","\\d","[[:punct:]]"),S,Vectorize(grepl))
S[!colSums(y[(s=rowSums(y))=="if"(B,max,min)(s),,drop=F])]=" "
cat(S,sep='')}


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:

y=mapply(grepl,c("[a-z]","[A-Z]","\\d","[^a-zA-Z0-9]"),list(S))

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.

• 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)) – NofP Oct 31 '17 at 15:16
• @NofP oh, very nice. Also, you can surround code with backticks () to have it display like this. :) – Giuseppe Oct 31 '17 at 15:22
• The rules state that you can use a character array or list as input , so you could probably remove the S=el(strsplit(G,"")) – MickyT Oct 31 '17 at 22:10
• @MickyT ah, I overlooked that, thank you. – 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!

## Explanation

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.


# 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.

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

# Jelly, 35 bytes

ØWṖs26µẎØṖḟW⁸;
¢ċ@€S¥€N⁹¡Mị¢Ẏ⁸f€ȯ€⁶


Try it online!

# Java (OpenJDK 8), 448439432362361354352348343 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!

• 366 bytes – Kevin Cruijssen Oct 31 '17 at 13:56
• You can remove the + in \\|+$ for an additional -1 byte. – Kevin Cruijssen Oct 31 '17 at 14:05 • 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(".$","")," ");}. – Kevin Cruijssen Oct 31 '17 at 14:17
• Oh, and n=s.length() can be n=-1>>>1 for an additional -4. – Kevin Cruijssen Oct 31 '17 at 14:22
• Oh, one more small thing to golf: [0-9] -> \\d – 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!

## Ungolfed

->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 : " "
}
}

• Very cool answer! You can use -1 as the “highest” value and replace minmax[t] by sort[t]. – Lynn Nov 1 '17 at 17:10

# Python 2, 190183174 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!

• 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? – James Nov 1 '17 at 17:06
• @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. – dylnan Nov 1 '17 at 17:13
• 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. – James Nov 1 '17 at 17:17
• 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! – James Nov 1 '17 at 17:19
• @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. – dylnan Nov 1 '17 at 17:21

## JavaScript (ES6), 151 149 bytes

g=
(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.

# Jelly, 37 bytes

ØWṖs26µẎØṖḟW⁸;
¢f@³L$ÐṂFe@Ð€³¬¹⁴?a³o⁶  Try it online! -6 bytes "borrowing" from Erik's post :D • Lol preserving the spaces is essentially half the program :D – Mr. Xcoder Oct 31 '17 at 13:21 • Could you add an explanation, or are you still working on golfing it first? – Kevin Cruijssen Oct 31 '17 at 13:21 • @KevinCruijssen Golfing first :D – HyperNeutrino Oct 31 '17 at 13:38 # Java (OpenJDK 8), 307 + 34306 + 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! Explanation: String t=s.replaceAll("\\d","2") .replaceAll("[a-z]","0") .replaceAll("[A-Z]","1") .replaceAll("\\D","3")  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. t.chars().forEach(j->{a[j%4]++;}); 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){ z=x>z?x:z; y=x<y?x:y; }  This then loops through the counts of each group stored in the array and calculates the minimum (y) and the maximum (z). for(;i<s.length();i++) 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! # Bash, 229227 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 • 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(){((. – Kevin Cruijssen Oct 31 '17 at 16:11 • yes, space is mandatory generally except for (, also 2 bytes could be saved using ( instead of {, degrading performance because creating a subshell – 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++])echog[$$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. • @KevinCruijssen Make sure you have the latest PHP version (7.1.0) selected. – 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 f= 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}"))
.as-console-wrapper{max-height:100%!important}