Histogramme Alphabet

33

Avec une phrase d'entrée composée d'un ou plusieurs mots [a-z]+et de zéro ou plusieurs espaces , générez un histogramme (graphique à barres) à la technique ASCII de la distribution des lettres de la phrase d'entrée.

L’histogramme doit être disposé horizontalement, c’est-à-dire avec la lettre en bas à gauche dans l’ordre alphabétique, de gauche à droite, avec un axe des Y libellé 1-et toutes les 5 unités. L'axe des ordonnées doit être le plus petit multiple de cinq qui soit au moins aussi grand que la barre la plus haute et doit être aligné à droite. L'axe des abscisses est étiqueté avec les lettres saisies, sans espace entre eux. Par exemple, l'entrée a bb dddoit avoir un libellé abdet non pas ab d, en sautant le c. Les barres elles-mêmes peuvent être constituées de n'importe quel caractère ASCII cohérent - je vais utiliser Xici dans mes exemples.

test example

5-

   X
   X   X
1-XXXXXXXX
  aelmpstx

Puisqu'il y en a trois e, deux tet un de almsx.

Plus d'exemples:

the quick brown fox jumped over the lazy dogs

5-
      X         X
      X         X
     XX  X      X  X XX
1-XXXXXXXXXXXXXXXXXXXXXXXXXX
  abcdefghijklmnopqrstuvwxyz


now is the time for all good men to come to the aid of their country

10-
              X
              X
              X  X
      X       X  X
 5-   X       X  X
      X   X   X  X
      X  XX XXXX X
   XXXXX XXXXXXX X
 1-XXXXXXXXXXXXXXXXXX
   acdefghilmnorstuwy

a bb ccc dddddddddddd

15-


      X
      X
10-   X
      X
      X
      X
      X
 5-   X
      X
     XX
    XXX
 1-XXXX
   abcd

a bb ccccc

5-  X
    X
    X
   XX
1-XXX
  abc

I / O et règles

  • La saisie peut être effectuée dans n’importe quel format raisonnable et par n’importe quelle méthode convenable . Cela signifie également que vous pouvez prendre des entrées en majuscules, si cela est plus logique pour votre code.
  • Les nouvelles lignes et les autres espaces sont facultatifs, à condition que les caractères s'alignent correctement.
  • Un programme complet ou une fonction sont acceptables. Si une fonction est utilisée, vous pouvez renvoyer la sortie plutôt que de l’imprimer.
  • La sortie peut être vers la console, renvoyée sous forme de liste de chaînes, sous forme de chaîne unique, etc.
  • Les échappatoires standard sont interdites.
  • Il s’agit du donc toutes les règles de golf habituelles s’appliquent et le code le plus court (en octets) gagne.
AdmBorkBork
la source
3
Je pense que ce serait un graphique à barres plutôt qu'un histogramme, étant donné qu'il s'agit de données catégoriques plutôt que numériques, mais je suis surtout pédant.
Giuseppe
est-ce que l'entrée est garantie d'être non vide?
dzaima
2
Être juste un pendentif, mais ce n'est pas un histogramme , c'est un graphique à barres. Encore un beau défi quand même!
Caird coinheringaahing
4
Une approche tuftienne consisterait à séparer les caractères représentés des barres et à ne pas avoir une rangée d'étiquettes séparée.
dmckee
2
Le caractère de l'histogramme doit être cohérent, mais dans tous les cas ou dans chaque cas?
Adám

Réponses:

7

R , 239 230 octets

K=table(el(strsplit(gsub(" ","",scan(,"")),"")));m=matrix(" ",L<-sum(K|1)+1,M<-(M=max(K))+-M%%5+1);m[2:L,M]=names(K);m[1,M-g]=paste0(g<-c(1,seq(5,M,5)),"-");m[1,]=format(m[1,],j="r");for(X in 2:L)m[X,M-1:K[X-1]]=0;write(m,1,L,,"")

Essayez-le en ligne!

table fait le gros du travail ici, en identifiant les personnages, en les triant et en retournant leurs comptes.

Tout le reste consiste simplement à s'assurer que les compensations sont correctes pour l'impression, ce qui constitue le "vrai" travail d'un défi artistique ascii.

Merci à @dylnan pour avoir signalé un bug.

Grâce à @rturnbull pour l’ scanapproche, supprimant 2 octets.

Giuseppe
la source
237 octets
rturnbull
@rturnbull J'ai réussi à supprimer quelques octets supplémentaires après cela, merci!
Giuseppe
6

gnu sed -r, 516 490 278 249 + 1 octets

s/$/:ABCDEFGHIJKLMNOPQRSTUVWXYZ /
:a
s/(.)(:.*\1)/\2\1/I
ta
s/[A-Z]+/ /g
h
z
:b
s/ \w/ /g
G
s/:/&I/g
/:II{5}+ *$/M!bb
s/[a-z]/X/g
G
s/:(I{5}+|I)\b/0\1-/g
s/:I*/  /g
s/ (\w)\1*/\1/g
s/$/; 10123456789I0/
:c
s/(.)I(.*\1(I?.))|;.*/\3\2/
/\nI/s/^/ /Mg
tc

Essayez-le en ligne!


Je suis sûr que cela peut être amélioré , mais pour le moment, cela devrait être bon, étant donné qu’il est fait en sed, où vous n’avez pas d’arithmétique ou de tri natif. Alors j'ai menti, cela ne suffisait pas, alors je l'ai amélioré (réécrit) de 212 octets supplémentaires, avec une astuce concernant l'algorithme de tri de Cows quack , ce qui m'a donné l'idée de raccourcir également la conversion unaire en décimale.
Description du fonctionnement interne:

s/$/:ABCDEFGHIJKLMNOPQRSTUVWXYZ /
:a
s/(.)(:.*\1)/\2\1/I
ta
s/[A-Z]+/ /g
h
z

Ceci trie les entrées et sépare les groupes avec des espaces. Cela fonctionne en ajoutant d’abord un alphabet majuscule et un espace séparé par deux points à la fin. Ensuite, il déplace chaque caractère devant les deux points vers un caractère correspondant derrière les deux points en utilisant une substitution insensible à la casse dans une boucle. Les lettres majuscules sont ensuite remplacées par des espaces et la chaîne est copiée dans l'espace de stockage.

:b
s/ \w/ /g
G
s/:/&I/g
/:II{5}+ *$/M!bb

Cette boucle fonctionne en réduisant chaque groupe de caractères d'une taille, en ajoutant la ligne d'origine triée et en incrémentant les compteurs unaires après les deux points restants après le tri. Il boucle jusqu'à ce qu'une ligne vide portant le nombre 5 * n + 1 soit atteinte (puisque la dernière ligne génère des espaces). L'espace motif ressemble à ceci après la boucle:

:IIIIII           
:IIIII           
:IIII           
:III  e         
:II  ee     t    
:I a eee l m p s tt x   

Ensuite, le formatage suit:

s/[a-z]/X/g            # makes bars consistent
G                      # appends line that becomes x-axis
s/:(I{5}+|I)\b/0\1-/g  # moves zero in front of line 1 or 5-divisible
                       # lines for the decimal conversion and adds -
s/:I*/  /g             # removes numbers from other lines
s/ (\w)\1*/\1/g        # collapses groups of at least 1 into 1
                       # character, deleting the space before it
                       # so that only size-0-groups have spaces

Et enfin, le convertisseur unaire à décimal reste:

s/$/; 10123456789I0/
:c
s/(.)I(.*\1(I?.))|;.*/\3\2/
/\nI/s/^/ /Mg
tc

Il ajoute en gros une chaîne où se trouve la connaissance de la conversion. Vous pouvez l'interpréter comme suit: espace: -> 1 et 0-> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-> I0. L’expression de substitution s/(.)I(.*\1(I?.))|;.*/\3\2/fonctionne de la même manière que celle de tri, en remplaçant les caractères situés devant les [ (.)I] par le caractère situé à côté de celui situé à l’avant du I dans la chaîne de conversion [ (.*\1(I?.))]; la chaîne ajoutée [ |;.*]. La substitution [ /\nI/s/^/ /Mg] ajoute un remplissage si nécessaire.

Merci à Cows Quack pour avoir réduit la taille de 26 octets et pour l’algorithme de tri plus court.

Sad Sed
la source
Bienvenue sur PPCG, et bonne première réponse!
Kritixi Lithos
Vous pouvez utiliser \w(correspond aux caractères d'un mot) à plusieurs endroits pour enregistrer des octets. Aussi :b ... tbpeut simplement devenir s/\B\w/X/g. Vous pouvez supprimer la ligne qui la suit s/:/:,/gen modifiant les substitutions précédentes. Vous pouvez consulter goo.gl/JvD7Rs (lien TIO raccourci vers sed programme) pour voir ce que je veux dire.
Kritixi Lithos
1
Vous pouvez améliorer l’algorithme de tri, conseil: essayez d’ajouter zyx...cbaà l’entrée.
Kritixi Lithos
Brillant convertisseur unaire en décimal! Le vôtre est au moins 30 octets plus court que celui de Astuces pour le golf à Sed
Kritixi Lithos
5

Dyalog APL , 109 97 96 95 93 88 octets

{⊖(⍉r),⍨⍕↑(⊂'')@{1@0~⍵∊1,5×⍵}⍳≢⍉↑r←↑r,⍨⊂' -','   - '⍴⍨5×⌈5÷⍨≢1↓⍉↑r←↓{⍺,∊⍕¨0×⍵}⌸⍵[⍋⍵]∩⎕A}

Essayez-le en ligne!

A besoin ⎕IO←0

Way trop d'octets sauvés grâce à Adám et vaches charlatan !

dzaima
la source
Pour le dernier moment, vous pouvez essayer ⍵[⍋⍵]~' '(trie et supprime les espaces avant de passer )
Kritixi Lithos
'X'/⍨≢∊⍕¨×
Adám
et ⍵>0×⍵
Kritixi Lithos
Votre lien TIO a un en-tête inutile.
Adám
2⌷⍴≢⍉deux fois
Adám
5

05AB1E , 58 47 octets

áê©S¢Z5‰`Ā+L5*1¸ì'-«ð5×ý#À¦Áí.Bís'X×ζ‚ζJR»,®3ú,

Essayez-le en ligne!

-11 octets grâce à @Emigna

Urne Magique De Pieuvre
la source
Peut - être que cela pourrait aider? Vous n'avez pas le temps de les lier, mais peut-être qu'ils peuvent donner de l'inspiration.
Emigna
@ Emigna Je vais jeter un oeil, certainement différent du mien :).
Urne magique Octopus
@ Emigna 57 octets après l'avoir cousu ... étant donné que je n'ai pas trop essayé d'optimiser. Essayez-le en ligne!
Urne Magique Octopus
47 octets avec une restructuration et une optimisation.
Emigna
Vos lettres ne s'alignent pas avec les X pour certaines entrées. tio.run/##AVUAqv8wNWFiMWX// .....
mbomb007
3

Python 2 , 192 octets

s=input()
d={c:s.count(c)for c in s if' '<c}
h=-max(d.values())/5*-5
for y in range(h,-1,-1):print('%d-'%y*(y%5==2>>y)).rjust(len(`-h`))+''.join(('X '[y>v],k)[y<1]for k,v in sorted(d.items()))

Essayez-le en ligne!

Explication

La ligne 2 calcule les valeurs de l’histogramme de manière assez simple, en ignorant ' ' .

Ligne 3 utilise l'astuce de calcul ceil(x/5)comme -(-x/5): nous arrondissons la fréquence maximale jusqu'à multiple de 5 en utilisant la formule -x/5*-5. C'esth .

La ligne 4 est une boucle comptant de hbas en haut 0, en imprimant chaque ligne:

  • Si y%5==2>>ynous imprimons une étiquette. Lorsque cela esty {1, 5, 10, 15, 20,…}

    (Cette formule pourrait peut-être être plus courte. Nous avons simplement besoin de quelque chose qui vaut 1 ou Vrai pour {1, 5, 10,…}, et 0 ou Faux ou même un entier négatif pour toutes les autres valeurs de y.)

  • Nous justifions à juste titre l’étiquette (ou l’espace vide) en len(`-h`)espaces: c’est une sauvegarde nette d’un octet len(`h`)+1!

  • Ensuite, nous imprimons Xles espaces et les espaces pour cette ligne (si y≥ 1) ou les lettres (si y= 0), en passant par les paires clé-valeur ddans l'ordre croissant.

Lynn
la source
1
Belle création de ticks avec '%d-'%y*(y%5==2>>y). Cela vous dérange-t-il si j'utilise cela dans ma réponse?
Dylnan
-~-(y%5*~-y)fonctionne aussi mais malheureusement, c'est un octet de plus.
dylnan
2

Charbon de bois , 62 octets

≔E²⁷⟦⟧ηFθ⊞§η⌕βιι≔⊟ηθ≦LηP⭆β⎇§ηκιω↑↑ΦηιF÷⁺⁹⌈η⁵«≔∨×⁵ι¹ιJ±¹±ι←⮌⁺ι-

Essayez-le en ligne! Le lien est vers la version verbeuse du code. Explication:

≔E²⁷⟦⟧η

Créez une liste de 27 listes.

Fθ⊞§η⌕βιι

Poussez chaque caractère saisi dans la liste correspondant à sa position dans l’alphabet minuscule. Les caractères non minuscules sont poussés vers la 27e liste.

≔⊟ηθ

Jeter le 27ème élément de la liste.

≦Lη

Prendre la longueur de tous les éléments de la liste.

P⭆β⎇§ηκιω

Imprimez les lettres minuscules correspondant aux éléments de la liste non nuls.

↑↑Φηι

Imprimer les éléments de la liste non nuls vers le haut. Puisqu'il s'agit d'un tableau d'entiers, chaque entier s'imprime sous la forme d'une ligne (maintenant verticale), chacune dans une colonne séparée.

F÷⁺⁹⌈η⁵«

Calculez le nombre de graduations sur l’axe Y et passez-les en boucle.

≔∨×⁵ι¹ι

Calculez la position du prochain tick.

J±¹±ι

Passez au prochain tick.

←⮌⁺ι-

Imprimez la coche inversée et de face à face, en l'alignant correctement à droite.

Neil
la source
2

Gelée , 48 octets

Quel champ de mines à parcourir!

J’⁶D;”-Ɗ%5^ỊƲ?€Uz⁶ZU
ḟ⁶ṢµĠ¬;⁶$L%5Ɗ¿€;"@Qz⁶Ç;"$ṚY

Un programme complet imprimant le résultat (sous forme de lien monadique, il renverrait une liste contenant des caractères et des entiers [0,9])

Essayez-le en ligne! Ou voir la suite de tests

Comment?

J’⁶D;”-Ɗ%5^ỊƲ?€Uz⁶ZU - Link 1, get y-axis: list of columns (including x-axis & top-spaces)
J                    - range of length  [1,2,3,4,5,6,...,height+1] (+1 for x-axis)
 ’                   - decrement        [0,1,2,3,4,5,...] (line it up with the content)
             ?€      - if for €ach...
            Ʋ        - ...condition: last four links as a monad:
        %5           -   modulo by five
           Ị         -   insignificant? (1 for 0 and 1, else 0)
          ^          -   XOR (0 for 1 or multiples of 5 greater than 0, else 0)
  ⁶                  - ...then: literal space character
       Ɗ             - ...else: last three links as a monad:
   D                 -   decimal list of the number, e.g. 10 -> [1,0]
     ”-              -   literal '-' character
    ;                -   concatenate, e.g. [1,0,'-']
               U     - upend (reverse each)
                z⁶   - transpose with a filler of space characters
                  Z  - transpose
                   U - upend (i.e. Uz⁶ZU pads the left with spaces as needed)

ḟ⁶ṢµĠ¬;⁶$L%5Ɗ¿€;"@Qz⁶Ç;"$ṚY - Main link: list of characters
ḟ⁶                          - filter out space characters
  Ṣ                         - sort
   µ                        - start a new monadic chain, call that S
    Ġ                       - group indices of S by their values
     ¬                      - logical NOT (vectorises) (getting 0 for the X "characters")
             ¿€             - while for €ach...
            Ɗ               - ...condition: last three links as a monad:
         L                  -   length
          %5                -   modulo by five
        $                   - ...do: last two links as a monad:
      ;⁶                    -   concatenate a space character
                  Q         - deduplicate S (get the x-axis)
               ;"@          - zip with (") concatenation (;) with swapped arguments (@)
                   z⁶       - transpose a with filler of space characters
                        $   - last two links as a monad:
                     Ç      -   call last link (1) as a monad (get y-axis)
                      ;"    -   zip with concatenation (complete the layout)
                         Ṛ  - reverse (otherwise it'll be upside-down)
                          Y - join with newlines
                            - implicit print
Jonathan Allan
la source
2

Ruby , 250 248 234 188 173 157 157 153 octets

->s{a=s.scan(/\w/).sort|[]
m=-(c=a.map{|l|s.count l}).max/5*-5
m.downto(1).map{|i|(i%5<1||i<2?"#{i}-":'').rjust(m)+c.map{|l|l<i ?' ':?X}*''}<<' '*m+a*''}

Essayez-le en ligne!

Grâce à:

  • dylnan pour -16 octets avec un rembourrage moins strict
  • Lynn pendant -2 octets en arrondissant avec-x/5*-5
  • Kirill L. pour -2 octets en obtenant des éléments de tableau uniques avec|[]
Nnnes
la source
2

Java (JDK 10) , 296 octets

s->{int d[]=new int[26],m=0;char a;for(int c:s.getBytes())m=c>32&&++d[c-=65]>m?(d[c]+4)/5*5:m;String r=m+"-",z=r.replaceAll("."," ");for(;m>0;r+="\n"+(--m%5<1|m==1&&m>0?z.format("%"+~-z.length()+"s-",m):z))for(a=0;a<26;a++)r+=d[a]>0?m>d[a]?" ":"x":"";for(a=64;a++<90;)r+=d[a-65]>0?a:"";return r;}

Essayez-le en ligne!

Crédits

Olivier Grégoire
la source
@aoemica Correct. Je l'ai corrigé.
Olivier Grégoire
1
Ce n'est pas beaucoup, mais vous pouvez économiser 2 octets. --m%5==0peut être --m%5<1, parce que vous avez aussi le &m>0chèque. Et m<=d[a]?"x":" "peut être m>d[a]?" ":"x".
Kevin Cruijssen
@KevinCruijssen 2 octets sont 2 octets! Je ne pense pas qu'il y ait plus de golf, à l'exception d'un algorithme différent.
Olivier Grégoire
1
1 octet plus en changeant (--m%5<1|m==1)&m>0à--m%5<1|m==1&&m>0
Kevin Cruijssen
1

Pyth, 65 octets

J.tm+ed*hd\Xr8S-Qd)=+J*]d%_tlJ5_.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ

Essayez-le ici

Explication

J.tm+ed*hd\Xr8S-Qd)=+J*]d%_tlJ5_.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ
J.tm+ed*hd\Xr8S-Qd)
     Get the bars.
                   =+J*]d%_tlJ5
     Round up the height to the next number that's 1 mod 5.
                               _.e+?q<k2%k5.F"{:{}d}-",klQ*dhlQbJ
     Stick the axis labels on.

la source
1

JavaScript (Node.js) , 262 256 octets

* Merci à @Shaggy pour la réduction de 2 octets

a=>[...a].map(x=>x>" "&&(d=c[x]=(c[x]||x)+"X")[m]?m=d.length-1:0,c={},m=i=0)&&Object.keys(c).sort().map(x=>[...c[x].padEnd(m)].map((l,j)=>A[m-j-1]+=l),A=[...Array(m+=6-m%5)].map(x=>(++i>=m||((D=m-i)%5&&m-i^1)?"":D+"-").padStart((m+" ").length)))&&A.join`
`

Essayez-le en ligne!

DanielIndie
la source
Quelques économies rapides que je peux repérer sur mon téléphone: 1.Prenez les entrées comme un ensemble de caractères individuels, 2.Remplacer x!=" "par x>" ".
Shaggy le
3.Remplacer m=0par i=m=0et map((x,i)=>avec map(x=>.
Shaggy
1

Python 2 , 249 224 219 215 205 197 197 187 188 182 176 octets

def f(s):S=sorted(set(s)^{' '});C=map(s.count,S);P=max(C)+4;return zip(*(zip(*[('%d-'%y*(y%5==2>>y)).rjust(P)for y in range(P,0,-1)])+[(n*'#').rjust(P)for n in C]))+[[' ']*P+S]

Essayez-le en ligne!

Renvoie une liste de listes de caractères représentant des lignes.

  • Enregistré quelques octets en incluant beaucoup d'espaces supplémentaires.
  • Had an unnecessary map(list,yticks) in there.
  • Changed space padding to save some bytes.
  • I thought I was sorting but I was not: +2 bytes. But I saved one independently at the same time. y==1 replaced by y<2.
  • -6 bytes thanks to Lynn by using '%d-'%y*(y%5==2>>y) instead of (`y`+'-')*(not y%5or y<2).

Slightly ungolfed:

def f(s):
	S=sorted(set(s)^{' '})  # sorted list of unique letters (without ' ')
	C=map(s.count,S)        # count of each unique letter in the input
	P=max(C)+4              # used for padding and getting highest y tick
	B=[(n*'#').rjust(P)for n in C]     # create bars
	yticks = [('%d-'%y*(y%5==2>>y)).rjust(P)for y in range(P,0,-1)]  # create y ticks at 1 and multiples of 5
	yticks = zip(*yticks)                      # need y ticks as columns before combining with bars
	return zip(*(yticks+B))+[[' ']*P+S]        # zip ticks+bars then add row of sorted unique letters.
dylnan
la source
1

C# (.NET Core), 344 340 338 + 18 bytes

Includes 18 bytes for using System.Linq;

Saved 6 bytes thanks to @KevinCruijssen.

n=>{var l=n.Where(c=>c!=32).GroupBy(c=>c).OrderBy(c=>c.Key).ToDictionary(k=>k.Key,c=>c.Count());int h=(l.Values.Max()/5+1)*5,o=(h+"").Length+1,m=l.Keys.Count+o,t=h+1,i=0,j;var a=new string[t];for(string p,q;i<t;a[i++]=q)for(q=(p=i>0&i%5<1|i==1?i+"-":"").PadLeft(j=o);j<m;){var c=l.ElementAt(j++-o).Key;q+=i<1?c:l[c]>=i?'X':' ';}return a;}

Try it online!

Ian H.
la source
You have a space at j< m; that can be removed. And int i=0,j can be placed as ,i=0,j after the other ints for -4 bytes in total. You'll have to including the 18 bytes for the using System.Linq; however..
Kevin Cruijssen
@KevinCruijssen Thanks, I missed these. And I added the 18 bytes.
Ian H.
+1 from me. Oh, and you can save 2 more bytes by changing for(;i<t;){string p=i>0&i%5<1|i==1?i+"-":"",q=p.PadLeft(o);for(j=o;j<m;){...}a[i++]=q;} to for(string p,q;i<t;)for(p=i>0&i%5<1|i==1?i+"-":"",q=p.PadLeft(j=o);j<m;a[i++]=q){...}. Try it online.
Kevin Cruijssen
@KevinCruijssen Thats really clever, thanks!
Ian H.
1

Bash + coreutils, 332 324 323 318 312 302 298 296 293 291 bytes

c()(cut -d\  -f$@)
p=printf
cd `mktemp -d`
grep -o [^\ ]<<<$@|sort|uniq -c|c 7->a
sort -k2<a>b
r=$[`c 1 <a|sort -n|tail -1`+5]
s=${#r}
t()($p \ ;((i++<s))&&t;i=)
for((;--r;));{
((r%5&&r>1))&&t||$p %${s}s- $r;IFS='
'
for l in `<b`;{ ((r<=`c 1 <<<$l`))&&$p X||$p \ ;}
echo
}
t
c 2 <b|tr -d \\n

Try it online!

Annotated:

c()(cut -d\  -f$@)
p=printf              # saving a few bytes

cd `mktemp -d`        # for temp files

grep -o [^\ ]<<<$@    # grabs all non-space characters
    |sort|uniq -c     # get character frequency
    |c 7->a           # slightly hacky way of stripping leading spaces;
                      #     uniq -c adds 6 spaces in front of each line

sort -k2<a>b          # store frequencies sorted alphabetically in b

r=$[`                 # r = highest frequency +5:
    c 1 <a            #     get frequencies
    |sort -n|tail -1  #     get maximum frequency
    `+5]              #     +4 so at least one multiple of 5 is
                      #     labeled, +1 because r gets pre-decremented

s=${#r}                    # s = length of r as a string
t()($p \ ;((i++<s))&&t;i=) # pad line with s+1 spaces

for((;--r;));{         # while (--r != 0)
    ((r%5&&r>1))&&     # if r isn't 1 or a multiple of 5
        t||            #     then indent line 
        $p %${s}s- $r; # otherwise print right-aligned "${r}-"
        IFS='
'                      # newline field separator
    for l in `<b`;{          # for all letters and their frequencies:
        ((r<=`c 1 <<<$l`))&& #     if frequency <= current height 
            $p X||           #         then print an X
            $p \ ;}          #     otherwise print a space
    echo
}
t # indent x-axis labels
c 2 <b|tr -d \\n # print alphabetically sorted characters

Thanks to @IanM_Matrix for saving 3 bytes.

user9549915
la source
cat b could be <b saving 3 characters
IanM_Matrix1
0

C, 201 bytes

char c[256],*p,d;main(int a,char **b){for(p=b[1];*p;p++)++c[*p|32]>d&*p>64?d++:0;for(a=(d+4)/5*5;a+1;a--){printf(!a||a%5&&a!=1?"    ":"%3i-",a);for(d=96;++d>0;c[d]?putchar(a?32|c[d]>=a:d):0);puts(p);}}

Input is taken from the command line (first argument). Uses exclamation marks instead of X's to further reduce code size. Counter on the left is always three characters long.

Tested with GCC and clang.

Simon
la source
for(p=b[1];*p;p++) can most likely be for(p=b[1]-1;*++p;), main(int a,char **b) could probably be golfed to m(a,b)char**b;.
Jonathan Frech
Since a!=1 will be boolean, a%5&&a!=1? should be equivalent to a%5&a!=1? or a%5&&~-a.
Jonathan Frech
0

Excel VBA, 316 bytes

An Anonymous VBE immediate window function that takes input from cell [A1] and outputs to the VBE immediate window.

For i=1To 26:Cells(2,i)=Len(Replace([Upper(A1)],Chr(i+64),11))-[Len(A1)]:Next:m=-5*Int(-[Max(2:2)]/5):l=Len(m)+1:For i=-m To-1:?Right(Space(l) &IIf(i=-1Xor i Mod 5,"",-i &"-"),l);:For j=1To 26:?IIf(Cells(2,j),IIf(Cells(2, j) >= -i, "X", " "),"");:Next:?:Next:?Spc(l);:For i=1To 26:?IIf(Cells(2,i),Chr(i+96),"");:Next

Ungolfed Version

Public Sub bar_graph()
    For i = 1 To 26
        ''  gather the count of the letter into cells
        Cells(2, i) = Len(Replace([Upper(A1)], Chr(i + 64), 11)) - [Len(A1)]
    Next
    m = -5 * Int(-[Max(2:2)] / 5)   ''  get max bar height
    l = Len(m) + 1                  ''  length of `m` + 1
    For i = -m To -1
        ''  print either a label or free space (y-axis)
        Debug.Print Right(Space(l) & IIf((i = -1) Xor i Mod 5, "", -i & "-"), l);
        For j = 1 To 26
            ''  print 'X' or ' ' IFF the count of the letter is positive
            If Cells(2, j) Then Debug.Print IIf(Cells(2, j) >= -i, "X", " ");
        Next
        Debug.Print                 ''  print a newline
    Next
    Debug.Print Spc(l);             ''  print spaces
    For i = 1 To 26
        ''  print the letters that were used (x-axis)
        Debug.Print IIf(Cells(2, i), Chr(i + 96), "");
    Next
End Sub
Taylor Scott
la source
0

Perl 5 -n, 198 168 bytes

s/[a-z]/$\<++${$&}?$\=${$&}:0/eg;$\++while$\%5;$;=1+length$\++;printf"%$;s".'%s'x26 .$/,$\%5&&$\-1?"":"$\-",map$$_>=$\?X:$$_&&$",a..z while--$\;say$"x$;,map$$_&&$_,a..z

Try it online!

Xcali
la source
0

Python 3, 177 bytes

lambda s:[[list(("%d-"%i*(i%5==2>>i)).rjust(len(q)))+["* "[s.count(c)<i]for c in q]for i in range(max(map(s.count,q))+4,0,-1)]+[[" "]*len(q)+q]for q in[sorted(set(s)-{' '})]][0]

Try it online!

This may not be the most byte-efficient approach in Python, but I really wanted to solve this with a "true one-liner" lambda.

Outputs a list of character lists. Abuses multiple leading newlines and spaces just like everybody else. It may actually be further reduced to 174 bytes if it is acceptable to wrap the result in another list, so that we could transfer the final [0] indexing to the footer.

Kirill L.
la source
0

JavaScript (ES8), 200 bytes

Takes input as an array of characters. Returns a string.

s=>(s.sort().join``.replace(/(\w)\1*/g,s=>a.push(s[0]+'X'.repeat(l=s.length,h=h<l?l:h)),h=a=[]),g=y=>y--?(y<2^y%5?'':y+'-').padStart(`${h}_`.length)+a.map(r=>r[y]||' ').join``+`
`+g(y):'')(h+=5-~-h%5)

Try it online!

Commented

s => (                    // s[] = input array of characters (e.g. ['a','b','a','c','a'])
  s.sort()                // sort it in lexicographical order (--> ['a','a','a','b','c'])
  .join``                 // join it (--> 'aaabc')
  .replace(/(\w)\1*/g,    // for each run s of consecutive identical letters (e.g. 'aaa'):
    s => a.push(          //   push in a[]:
      s[0] +              //     the letter, which will appear on the X-axis
      'X'.repeat(         //     followed by 'X' repeated L times
        L = s.length,     //     where L is the length of the run (--> 'aXXX')
        h = h < L ? L : h //     keep track of h = highest value of L
    )),                   //   initialization:
    h = a = []            //     h = a = empty array (h is coerced to 0)
  ),                      // end of replace() (--> a = ['aXXX','bX','cX'] and h = 3)
  g = y =>                // g = recursive function taking y
    y-- ?                 //   decrement y; if there's still a row to process:
      (                   //     build the label for the Y-axis:
        y < 2 ^ y % 5 ?   //       if y != 1 and (y mod 5 != 0 or y = 0):
          ''              //         use an empty label
        :                 //       else:
          y + '-'         //         use a mark
      ).padStart(         //     pad the label with leading spaces,
        `${h}_`.length    //     using the length of the highest possible value of y
      ) +                 //     (padStart() is defined in ECMAScript 2017, aka ES8)
      a.map(r => r[y]     //     append the row,
                 || ' ')  //     padded with spaces when needed
      .join`` + `\n` +    //     join it and append a linefeed
      g(y)                //     append the result of a recursive call
    :                     //   else:
      ''                  //     stop recursion
)(h += 5 - ~-h % 5)       // call g() with h adjusted to the next multiple of 5 + 1
Arnauld
la source