Lundi Mini-Golf # 2: tronquer le texte long

25

Monday Mini-Golf: Une série de courts défis de , affichés (espérons-le!) Tous les lundis.

De nombreuses applications Web (en particulier les médias sociaux) tronquent automatiquement de longs passages de texte afin qu'ils tiennent dans le formatage de l'application. Dans ce défi, nous allons créer un algorithme pour couper automatiquement un passage de texte à une certaine longueur.

Défi

L'objectif du défi est d'écrire un programme ou une fonction qui accepte deux arguments:

  • T , le texte à tronquer.
  • L , la longueur maximale à retourner.

Et renvoie T , tronqué avec la logique suivante:

  • Si la longueur de T est inférieure ou égale à L , aucune troncature n'est nécessaire. Renvoie la chaîne d'origine.
  • Tronquer T à la longueur L -2. Si celui-ci ne contient ni espaces ni tirets, renvoyez T tronqué à exactement L -3 caractères, suivi d'une ellipse ....
  • Sinon, coupez la fin du résultat jusqu'au dernier espace ou trait d'union. Ajoutez des points de suspension ...et renvoyez le résultat.

Détails

  • T et L peuvent être pris dans n'importe quel ordre et dans n'importe quel format.
  • Vous pouvez supposer que 3 < L <2 31 .
  • Vous ne pouvez pas utiliser les points de suspension horizontaux U + 2026 ; vous devez utiliser trois périodes.
  • L'entrée ne commencera pas par un espace ou un trait d'union.
  • L'entrée ne contiendra aucun espace autre que des espaces réguliers. (Pas d'onglets, de nouvelles lignes, etc.)

Cas de test

Contributions:

"This is some very long text." 25
"This-is-some-long-hyphen-separated-text." 33
"Programming Puzzles & Code Golf is a question and answer site for programming puzzle enthusiasts and code golfers." 55 
"abcdefghijklmnopqrstuvwxyz" 20
"a b c" 4
"Very long." 100

Les sorties:

"This is some very long..."
"This-is-some-long-hyphen..."
"Programming Puzzles & Code Golf is a question and..."
"abcdefghijklmnopq..."
"a..."
"Very long."

(Notez que les guillemets sont juste pour spécifier qu'il s'agit de chaînes; elles n'ont pas besoin d'être incluses.)

Notation

Il s'agit de , donc le code valide le plus court en octets l'emporte. Tiebreaker va à la soumission qui a atteint son nombre d'octets final en premier. Le gagnant sera choisi lundi 5 octobre prochain. Bonne chance!

Edit: Félicitations à votre gagnant, @Jakube avec Pyth à nouveau, avec 25 octets!

ETHproductions
la source
7
Les réponses à ce défi devraient être une caractéristique standard dans leurs langues respectives. Trop souvent, j'ai vu une interface utilisateur qui présente une mauvaise troncature ...
Sanchises
1
... "Sinon, ajustez la fin du résultat jusqu'à" NON ", y compris le dernier espace ou trait d'union." Droite?
anatolyg
Le texte aura-t-il des onglets?
kirbyfan64sos
@anatolyg Non, car alors l'espace ou le trait d'union final apparaîtrait avant les points de suspension.
ETHproductions
@ kirbyfan64sos Non. J'ajouterai cela à la section Détails.
ETHproductions

Réponses:

12

Pyth, 25 octets

+WnzK<zeo}@zN" -"-Q2K"...

Essayez-le en ligne: démonstration ou suite de tests

Explication:

+WnzK<zeo}@zN" -"-Q2K"...  implicit: z = input string, Q = input number
        o        -Q2       order the indices N in [0, 1, ..., Q-3] by
         }@zN" -"            z[T] in " -"
                           (hyphen-indices get sorted to the back)
       e                   take the last such number
     <z                    reduce z to length ^
    K                      save this string to K
+WnzK               K"...  print (K + "...") if z != K else only K
Jakube
la source
4
J'aime la façon dont le code se termine à la fin ...
mathmandan
7

Perl, 69 59 52 octets

Code de 51 octets + ligne de commande de 1 octet. Suppose que l'entrée numérique peut être donnée avec le paramètre -i.

s/.{$^I}\K.*//&&s/(^([^ -]*).|.*\K[ -].*)..$/$2.../

Usage:

echo "This-is-some-long-hyphen-separated-text." | perl -p -i"33" entry.pl
Jarmex
la source
7

Python 2, 78 73 octets

t,l=input()
u=t[:l-2]
print(t,u[:max(map(u.rfind,' -'))]+'...')[l<len(t)]

Le format d'entrée suit l'exemple d'entrée.

xsot
la source
1
Un nom familier de Anarchy Golf. Bienvenue!
xnor
7

JavaScript (ES6), 123 78 67 61 octets

Je ne m'attendais pas à pouvoir réduire cela autant, mais il s'avère que le combo épissage / remplacement est capable de couvrir tous les cas où la troncature est nécessaire.

(T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):T

Le premier argument est la chaîne, le second est la longueur. Un merci spécial à edc65 pour l'optimisation du contrôle de longueur!

Voici le code d'origine (123 octets):

(T,L)=>(T.length>L?(S=T.slice(0,L)).slice(0,(m=Math.max(S.lastIndexOf` `,S.lastIndexOf`-`))<0?L-3:Math.min(L-3,m))+'...':T)
Mwr247
la source
4
Intelligent! +1. Astuce: souvent, vous n'avez pas besoin .lengthde vérifier la longueur d'un (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tscore de chaîne 61
edc65
@ edc65 Doh! Je cherchais une optimisation sur la vérification de la longueur, pensant qu'il devait y avoir un moyen de réduire cela, mais votre méthode ne m'était pas venue à l'esprit. Excellente suggestion! : D
Mwr247
Vous pouvez remplacer [ -][^ -]par \s\Spour économiser 5 octets supplémentaires
Shaun H
Excellente solution! @ShaunH, s'il le fait, cela ne fonctionnera pas pour les tirets, sûrement?
Jarmex
@Jarmex Silly brain, oui, ce n'est certainement pas le cas.
Shaun H
5

TI-BASIC, 87 octets

Prompt L,Str1
For(X,1,L
{inString(Str1," ",X),inString(Str1,"-",X
max(I,max(Ans*(Ans≤L-3->I
End
Str1
If L<length(Ans
sub(Ans,1,I+(L-3)not(I))+"...
Ans

TI-BASIC n'a pas beaucoup de commandes de manipulation de chaînes, nous devons donc trouver le dernier index manuellement: si la chaîne ne contient pas la chaîne à rechercher, inString(renvoie 0. Nous recherchons des tirets et des espaces à partir de chaque position à partir de 1 à Let enregistrer le plus grand nombre inférieur ou égal à L-3. Si ce nombre Iest toujours 0, nous utilisons L-3à la place comme index de fin.

En raison des limites de la calculatrice, le plus grand index adressable d'une chaîne est 9999; par conséquent, cela échouera pour les chaînes plus grandes.

Je compte sur le comportement de la calculatrice pour initialiser automatiquement la variable Ià 0, donc supprimez Iou effacez la mémoire de votre calculatrice avant de l'exécuter.

lirtosiast
la source
Il existe une solution plus courte utilisant des listes pour trouver le plus grand index, mais la limite de taille serait alors de ~ 500, pas 9999.
lirtosiast
4

C # .NET, 187 169 octets

Hmm ...

string f(string T,int L){if(T.Length<=L)return T;T=T.Substring(0,L-2);return T.Substring(0,T.Contains(" ")||T.Contains("-")?T.LastIndexOfAny(new[]{' ','-'}):L-3)+"...";}
Salah Alami
la source
oui bien sûr, je viens de supprimer des espaces pour diminuer les octets.
Salah Alami
3

Python 2, 105 octets

def t(s,l):a=s[:l-2];return s[:max(a.rfind(' '),a.rfind('-'))]+'...'if' 'in a or'-'in a else a[:-1]+'...'

Appelé avec

>>> print t("This is some very long text.", 25)
This is some very long...
Celeo
la source
1

Groovy, 95 octets

a={T,L->F=T.size()<=L?T:T[0..L-3]
m=F=~'(.*[- ])'
F==T?F:m?m[0][0].trim()+'...':F[0..-2]+'...'}

Assez simple, peut probablement être joué plus loin

Kleyguerth
la source
1

T-SQL, 145 octets

create proc a(@t varchar(max),@l int)as if LEN(@t)<=@l return @t;set @t = LEFT(@t,@l-3) select LEFT(@t,LEN(@t)-CHARINDEX('-',REVERSE(@t)))+'...'

usage:

exec a("This is some very long text.", 25) exec a("This-is-some-long-hyphen-separated-text.", 33)

Sam cd
la source
1

Ceylan 386 333 252 230 222 216 171 153 131 131 111

String t(String s,Integer l)=>s.size<l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains)else l-3)]+"...";

Original non golfé:

String truncate(String text, Integer length) {
    if(text.size < length) {
        return text;
    }
    Boolean spacePredicate(Character char) {
        return char == ' ' || char == '-';
    }
    Integer? spaceIndex = text[0:length-2].lastIndexWhere(spacePredicate);
    if(exists spaceIndex) {
        return text[0:spaceIndex] + "...";
    }
    return text[0:length-3]+"...";
}

Cela représente 386 octets / caractères. Quelques fonctionnalités intéressantes ici:

La x[y:z]syntaxe est du sucre syntaxique pour x.measure(y, z), et renvoie une sous-plage xcommençant ypar la longueur z- pour les chaînes, il s'agit d'une sous-chaîne. (Il existe également une x[y..z]syntaxe, qui est une plage allant de l'index y à z, tous deux inclus, ainsi que des plages semi-ouvertes x[...z]et x[y...].)

List.lastIndexWhere prend un prédicat (ie une fonction prenant un élément de liste et retournant un booléen, ie ici un Callable<Boolean, [Character]> ), et donne l'index du dernier élément de liste où le prédicat est rempli (ou nul, s'il n'est jamais rempli). Comme les chaînes sont des listes, cela fonctionne aussi pour les chaînes.

Le résultat de ceci, spaceIndexest de type Integer|Null, ou Integer?pour faire court - c'est-à-dire qu'il peut être soit un entier ou null(la seule valeur de type Null). (Le nom spaceIndexvient de quand je ne savais pas que -c'était aussi spécial - je suppose que ce breakIndexserait mieux.)

Avec exists spaceIndexnous pouvons vérifier si spaceIndexest non nul et faire quelque chose de différent alors. (À l'intérieur de ce bloc if, le compilateur sait qu'il n'est pas nul ... sans cela il se serait plaint si j'avais l'habitude spaceIndexd'accéder à la chaîne.)

Au lieu de la fonction locale, spacePredicatenous pouvons également utiliser une fonction anonyme

(Character char) => char == ' ' || char == '-'

Cela nous amène à 333 caractères:

String truncate(String text, Integer length) {
    if(text.size < length) {
        return text;
    }
    Integer? spaceIndex = text[0:length-2].lastIndexWhere(
        (Character char) => char == ' ' || char == '-');
    if(exists spaceIndex) {
        return text[0:spaceIndex] + "...";
    }
    return text[0:length-3]+"...";
}

La prochaine optimisation consiste à utiliser des noms de variables et de fonctions plus courts, ce qui nous ramène de 81 octets à 252:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    Integer? i = s[0:l-2].lastIndexWhere(
        (Character e) => e == ' ' || e == '-');
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

La fonction prédicat n'a pas besoin de déclarer son type d'argument, ce qui peut être déduit par le compilateur. Idem pour le type de i(où nous devons encore écrire valuepour le marquer comme une déclaration). Maintenant, cette déclaration est suffisamment courte pour tenir sur une seule ligne, ce qui nous ramène à 230:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere((e) => e == ' ' || e == '-');
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

Au lieu de e == ' ' || e == '-'nous pouvons également écrire e in [' ', '-'](ou e in {' ', '-'}, c'est un constructeur itérable au lieu d'un tuple). L' inopérateur est mappé à la méthode Category.contains, ce qui nous amène à l'idée que nous pouvons passer la containsméthode de ce tuple directement (c'est un appelable prenant n'importe quel objet, donc acceptant également le caractère), sans le passe- (e) => ...partout (222 octets):

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere([' ', '-'].contains);
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

En fait, une autre catégorie contenant les deux mêmes caractères est la chaîne de deux caractères " -". (De plus, il contient également ses sous-chaînes, mais cela ne fait pas de mal ici). 216 octets.

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere(" -".contains);
    if(exists i) {
        return s[0:i] + "...";
    }
    return s[0:l-3]+"...";
}

Je suppose que nous avons tiré le meilleur parti de cette ligne, passons aux autres ... les deux dernières déclarations de retour ont une certaine similitude que nous pouvons exploiter - elles diffèrent simplement par irapport à vs. l-3, et utilisent ijuste quand elle n'est pas nulle, sinon l-3. Heureusement, c'est exactement ce pour quoi l' elseopérateur est fait!

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    value i = s[0:l-2].lastIndexWhere(" -".contains);
    return s[0:(i else l-3)] + "...";
}

(Les parenthèses semblent être nécessaires ici, tout comme celles qui elseont une priorité plus faible que [:].) Il s'agit de 171 caractères. Maintenant, il in'est utilisé qu'une seule fois, nous pouvons donc l'intégrer, ce qui nous amène à 153 caractères:

String t(String s, Integer l) {
    if(s.size < l) {
        return s;
    }
    return s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";
}

Nous pouvons également remplacer cette if-return-returncombinaison par une combinaison des opérateurs thenet elseen un return. ( thenrenvoie est le deuxième opérande lorsque le premier est vrai, sinon null, ce qui permet ensuite elsede renvoyer son deuxième opérande.) 131 octets (bien que certaines des économies soient les espaces blancs dont nous nous débarrasserons de toute façon):

String t(String s, Integer l) {
    return s.size < l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";
}

Une fonction qui ne contient qu'un seul retour avec une expression peut alternativement être écrite avec la notation "grosse flèche", donnant 123:

String t(String s, Integer l) =>
    s.size < l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains) else l-3)] + "...";

Supprimer les espaces blancs inutiles nous donne les 111 derniers octets:

String t(String s,Integer l)=>s.size<l then s else s[0:(s[0:l-2].lastIndexWhere(" -".contains)else l-3)]+"...";

En complément, voici une fonction qui imprime les exemples de la question (en utilisant le nom tqui est utilisé après l'étape deux):

shared void testTruncate() {
    value testInputs = {
        ["This is some very long text.", 25],
        ["This-is-some-long-hyphen-separated-text.", 33],
        ["Programming Puzzles & Code Golf is a question and answer site for programming puzzle enthusiasts and code golfers.", 55], 
        ["abcdefghijklmnopqrstuvwxyz", 20],
        ["a b c", 4],
        ["Very long.", 100]
    };
    for(input in testInputs) {
        print(t(*input));
    }
}
Paŭlo Ebermann
la source
1

Shell POSIX + GNU sed, 65 octets

sed -re "s/(.{$1}).+/\1/;T;s/(.*)[- ]...*/\1.../;t;s/...$/.../;:"

C'est un travail fait pour sed! Mais j'avais besoin de shell pour obtenir la limite de longueur (peut-être que Perl serait mieux). La partie sed se développe en une séquence assez simple, avec des sauts conditionnels à la fin:

s/(.{$1}).+/\1/
T
s/(.*)[- ]...*/\1.../
t
s/...$/.../
:
Toby Speight
la source
1

Mathematica 192 octets

t=With[{r=StringTake[#,Min[#2-2,StringLength[#]]],p={"-",Whitespace},e="..."}, 
  Which[StringLength[#]<=#2,#,StringFreeQ[r,p],StringDrop[r,-1]<>e,
   True,StringTake[r,Max[StringPosition[r,p]]-1]<>e]]&

Appelé comme

t["This is some very long text.", 25]
Verbeia
la source
1

> <>, 74 octets

l$-:1)?\~r05.
/?=0:~$<-1
\}:0=  ?\::"- "@=@=+?
>~"..."r\}
/!?     <
>ol?!;

Cette solution nécessite que la chaîne soit tronquée et Lqu'elle se trouve déjà dans la pile, dans cet ordre.

Il y a 7 octets perdus causés par des problèmes d'alignement, essayant toujours de les jouer au golf.

Sok
la source
1

C # (157):

Basé sur la réponse de Salah Alami , mais plus court. La classe de chaîne dérive de IEnumerable<char>, donc au lieu de T.Contains(" ")||T.Contains("-"), j'utilise " -".Any(x=>T.Contains(x)).

Solution:

string f(string T,int L){if(T.Length<=L)return T;T=T.Substring(0,L-2);return T.Substring(0," -".Any(T.Contains)?T.LastIndexOfAny(new[]{' ','-'}):L-3)+"...";}

Non golfé:

string f (string T, int L)
{
    if (T.Length <= L)
        return T;

    T = T.Substring(0, L - 2);

    return T.Substring(0, " -".Any(T.Contains) ? T.LastIndexOfAny(new[]{' ', '-'}) : L - 3) + "...";
}

Mise à jour:

Enregistré 6 octets grâce au commentaire de SLuck49, en utilisant Any(T.Contains)au lieu de Any(x=>T.Contains(x)).

Abbas
la source
1
Car .Any(x=>T.Contains(x))vous pouvez directement utiliser la méthode au lieu d'un lambda comme .Any(T.Contains)pour économiser 6 octets
SLuck49
@ SLuck49 merci, mis à jour ma réponse.
Abbas
1

GS2 , 29 octets

Ce programme prend une entrée standard. La première ligne est la chaîne et la seconde est le numéro de longueur cible.

2a 0e 56 3c 40 a0 74 20 22 22 04 5d 2e 2a 3f 5b
20 2d 5d 7c 2e 07 2e 2e 2e 9d 20 e4 35

Le code GS2 peut parfois être un peu difficile à lire. :) Voici quelques commentaires.

2a         # lines - split input on newlines yielding a two element array
0e         # extract-array - pop array, push both elements 
56         # read-num - convert length string to number
3c         # take - truncate the string to specified length
40         # dup - duplicate truncated string on stack
a0         # junk1 - push the last popped value, the un-truncated string
74         # ne - test for inequality
    20     # reverse string
    22 22  # tail tail - remove first two characters

    # regex replace first occurrence of ".*?[ -]|." with "..."
    04 5d 2e 2a 3f 5b 20 2d 5d 7c 2e 07 2e 2e 2e 9d 

    20     # reverse string
e4         # block5 - make a block out of last 5 instructions
35         # when - conditionally execute block
récursif
la source
1

Groovy, 56 octets

Copie la réponse de Kleyguerth en premier, d'où les mêmes noms de variables ...

Coupez la chaîne de 2 caractères, puis la plupart du travail est effectué par l'expression régulière, remplacez un tiret ou un espace suivi d'un nombre quelconque de caractères qui ne sont pas un tiret ou un espace à la fin de la chaîne avec un "." OU remplacez n'importe quel caractère à la fin de la chaîne si tous les caractères qui le précèdent ne sont ni un tiret ni un espace avec un ".". Plus difficile à mettre en mots que d'écrire l'expression régulière ...

a={T,L->T.size()<=L?T:T[0..L-3].replaceAll("([- ][^ -]*|(?<=[^- ]*).)\$",".")+".."}

Edit: En fait, il suffit de supprimer la partie de la chaîne qui correspond à l'expression régulière et d'ajouter "..." à la fin:

a={T,L->T.size()<=L?T:T[0..L-3]-~/[- ][^ -]*$|.$/+"..."}
dbramwell
la source
1

Nettoyer , 89 octets

import StdEnv
$n l|size l>n=l%(0,last[n:[i\\i<-[2..n]&c<-:l|c==' '||c=='-']]-3)+++"..."=l

Essayez-le en ligne!

En tant que fonction $ :: Int String -> String

Οurous
la source
0

C # (Visual C # Interactive Compiler) , 117 octets

a=>b=>a.Length>b?a.Substring(0,(" -".Any(x=>a.IndexOf(x,0,b-2)>-1)?a.LastIndexOfAny(new[]{' ','-'},b-2):b-3))+"...":a

Basé sur @ Abba, qui est basé sur la réponse de @Salah Alami. Au lieu d'utiliser Containset un Substringappel inutile , il utilise IndexOf pour vérifier si un trait d'union ou un espace existe dans la chaîne tronquée.

Essayez-le en ligne!

Incarnation de l'ignorance
la source