Lunes Mini-Golf # 2: Truncar texto largo

25

Mini golf de lunes: una serie de desafíos de corto , publicados (¡ojalá!) Todos los lunes.

Muchas aplicaciones web (especialmente las redes sociales) truncan automáticamente largos pasajes de texto para que se ajusten al formato de la aplicación. En este desafío, vamos a crear un algoritmo para recortar automáticamente un pasaje de texto a una cierta longitud.

Reto

El objetivo del desafío es escribir un programa o función que tome dos argumentos:

  • T , el texto a truncar.
  • L , la longitud máxima para volver.

Y devuelve T , truncado con la siguiente lógica:

  • Si la longitud de T es menor o igual que L , no se necesita truncamiento. Devuelve la cadena original.
  • Truncar T a la longitud L -2. Si no contiene espacios ni guiones, devuelva T truncado a exactamente L -3 caracteres, seguido de puntos suspensivos ....
  • De lo contrario, recorte el final del resultado hasta el último espacio o guión. Agregue puntos suspensivos ...y devuelva el resultado.

Detalles

  • T y L pueden tomarse en cualquier orden y en cualquier formato.
  • Puede suponer que 3 < L <2 31 .
  • No puede usar puntos suspensivos horizontales U + 2026 ; debes usar tres períodos.
  • La entrada no comenzará con un espacio o un guión.
  • La entrada no contendrá ningún espacio en blanco que no sean espacios regulares. (Sin pestañas, líneas nuevas, etc.)

Casos de prueba

Entradas:

"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

Salidas:

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

(Tenga en cuenta que las comillas son solo para especificar que se trata de cadenas; no es necesario que se incluyan).

Tanteo

Este es el , por lo que gana el código válido más corto en bytes. Tiebreaker va a la presentación que alcanzó su conteo final de bytes primero. El ganador será elegido el próximo lunes 5 de octubre. ¡Buena suerte!

Editar: ¡ Felicidades a tu ganador, @Jakube con Pyth nuevamente, con 25 bytes!

ETHproducciones
fuente
77
Las respuestas a este desafío deberían ser una característica estándar en sus respectivos idiomas. Demasiado a menudo he visto una interfaz de usuario que presenta un mal truncamiento ...
Sanchises
1
... "De lo contrario, recorte el final del resultado hasta" NO ", incluido el último espacio o guión". ¿Correcto?
anatolyg
¿Tendrá el texto alguna pestaña?
kirbyfan64sos
@anatolyg No, porque entonces el espacio final o guión aparecería antes de los puntos suspensivos.
ETHproductions
@ kirbyfan64sos No. Agregaré eso a la sección Detalles.
ETHproductions

Respuestas:

12

Pyth, 25 bytes

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

Pruébelo en línea: Demostración o conjunto de pruebas

Explicación:

+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
fuente
44
Me gusta la forma en las pistas de código de descuento al final ...
mathmandan
7

Perl, 69 59 52 bytes

Código de 51 bytes + línea de comando de 1 byte. Asume que la entrada numérica se puede dar con el parámetro -i.

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

Uso:

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

Python 2, 78 73 bytes

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

El formato de entrada sigue la entrada de ejemplo.

xsot
fuente
1
Un nombre familiar de Anarchy Golf. ¡Bienvenido!
xnor
7

JavaScript (ES6), 123 78 67 61 bytes

No esperaba poder reducir esto tanto, pero resulta que el combo de empalme / reemplazo es capaz de cubrir todos los casos donde se necesita truncamiento.

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

El primer argumento es la cadena, el segundo es la longitud. ¡Un agradecimiento especial a edc65 por la optimización de verificación de longitud!

Aquí está el código original (123 bytes):

(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
fuente
44
¡Inteligente! +1. Consejo: a menudo no es necesario .lengthverificar la longitud de una (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tpuntuación de cadena 61
edc65
@ edc65 Doh! Había estado buscando una optimización en la verificación de longitud, pensando que tenía que haber alguna forma de reducirlo, pero su método no se me había ocurrido. Excelente sugerencia! : D
Mwr247
Puede reemplazar [ -][^ -]con \s\Spara guardar 5 bytes más
Shaun H
¡Gran solución! @ShaunH, si lo hace, no funcionará para los guiones, ¿no?
Jarmex
@Jarmex Cerebro tonto, sí, definitivamente no.
Shaun H
5

TI-BASIC, 87 bytes

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 no tiene muchos comandos de manipulación de cadenas, por lo que debemos encontrar el último índice de forma manual: si la cadena no contiene la cadena para buscar, inString( devuelve 0. Buscamos guiones y espacios que comienzan en cada posición desde 1 a Ly registrar el mayor número menor o igual a L-3. Si ese número Isigue siendo 0, usamos L-3como índice final en su lugar.

Debido a las limitaciones de la calculadora, el índice direccionable más grande de una cadena es 9999; por lo tanto, esto fallará para cadenas más grandes.

Confío en el comportamiento de la calculadora de inicializar automáticamente la variable Ia 0, por lo tanto, elimine Io borre la memoria de la calculadora antes de ejecutarla.

lirtosiast
fuente
Hay una solución más corta que usa listas para encontrar el mayor índice, pero luego el límite de tamaño sería ~ 500, no 9999.
lirtosiast el
4

C # .NET, 187 169 bytes

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
fuente
Sí, por supuesto, acabo de eliminar espacios para disminuir los bytes.
Salah Alami
3

Python 2, 105 bytes

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

Llamado con

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

Groovy, 95 bytes

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]+'...'}

Bastante sencillo, probablemente se pueda jugar más golf

Kleyguerth
fuente
1

CJam, 34 bytes

q~1$<_@={-2<_W%{" -"&}#~We|<'.3*}|

Pruébelo en línea: Chrome | Firefox

Dennis
fuente
1

T-SQL, 145 bytes

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)))+'...'

uso:

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

Sam cd
fuente
1

Ceilán 386 333 252 230 222 216 171 153 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 sin 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]+"...";
}

Esto es 386 bytes / caracteres. Algunas características interesantes aquí:

La x[y:z]sintaxis es azúcar sintáctica para x.measure(y, z), y devuelve un subrango de xinicio ycon longitud z; para cadenas, esta es una subcadena. (También hay una x[y..z]sintaxis, que es un intervalo desde el índice y a la z, ambos incluidos, así como tramos medio abiertos x[...z]y x[y...]).

List.lastIndexWhere toma un predicado (es decir, una función que toma un elemento de lista y devuelve un booleano, es decir, aquí un Callable<Boolean, [Character]> ), y proporciona el índice del último elemento de lista donde se cumple el predicado (o nulo, si nunca se cumple). Como las cadenas son listas, esto también funciona para las cadenas.

El resultado de esto spaceIndexes de tipo Integer|Null, o Integer?para abreviar, es decir, puede ser un número entero o null(el único valor de tipo Null). (El nombre spaceIndexproviene de cuando no me di cuenta de que eso -también era especial, supongobreakIndex que sería mejor).

Con exists spaceIndexpodemos verificar si spaceIndexno es nulo y hacer algo diferente entonces. (Dentro de este bloque si, el compilador sabe que no es nulo ... sin eso se habría quejado si hubiera usadospaceIndex acceder a la cadena).

En lugar de la función local spacePredicate, también podemos usar una función anónima

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

Esto nos lleva a 333 caracteres:

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 siguiente optimización es usar nombres más cortos de variables y funciones, lo que nos reduce en 81 bytes a 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 función de predicado en realidad no necesita su tipo de argumento declarado, que puede ser inferido por el compilador. Lo mismo para el tipo de i(donde todavía tenemos que escribir valuepara marcarlo como una declaración). Ahora esa declaración es lo suficientemente corta como para caber en una línea, llevándonos a 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]+"...";
}

En lugar de e == ' ' || e == '-', también podemos escribir e in [' ', '-'](o e in {' ', '-'}, este es un constructor iterable en lugar de uno de tupla). El inoperador se asigna al método Category.contains, lo que nos lleva a la idea de que podemos pasar el containsmétodo de esa tupla directamente (es invocable tomar cualquier objeto, por lo que también acepta caracteres), sin el (e) => ...repetitivo (222 bytes):

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 realidad, otra categoría que contiene los mismos dos caracteres es la cadena de dos caracteres " -". (Además, también contiene sus subcadenas, pero eso no duele aquí). 216 bytes.

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]+"...";
}

Supongo que sacamos el máximo provecho de esta línea, pasemos a las otras ... las dos últimas declaraciones de retorno tienen alguna similitud que podemos explotar: simplemente difieren en ivs. l-3y se usan isolo cuando no es nulo, de lo contrario l-3. Afortunadamente, ¡esto es exactamente para lo elseque está hecho el operador!

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)] + "...";
}

(Los paréntesis parecen ser necesarios aquí, ya que elsetiene una precedencia menor que [:]). Esto es 171 caracteres. Ahora ise usa solo una vez, por lo que podemos alinearlo, llevándonos a 153 caracteres:

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

También podemos reemplazar esta if-return-returncombinación por una combinación de los operadores theny elseen uno return. ( thendevuelve el segundo operando cuando el primero es verdadero, de lo contrario es nulo, lo que luego permite elsedevolver su segundo operando.) 131 bytes (aunque algunos de los ahorros son los espacios en blanco que eliminaremos de todos modos):

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

Una función que contiene solo un retorno con una expresión puede escribirse alternativamente con la notación de "flecha gorda", dando 123:

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

Eliminar el espacio en blanco innecesario nos da los 111 bytes finales:

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

Además, aquí hay una función que imprime los ejemplos de la pregunta (usando el nombre tque se usa después del paso dos):

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
fuente
1

POSIX shell + GNU sed, 65 bytes

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

Este es un trabajo hecho para sed! Pero necesitaba un shell para obtener el límite de longitud (quizás Perl sería mejor). La parte sed se expande a una secuencia bastante simple, con saltos condicionales cuando terminamos:

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

Mathematica 192 bytes

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]]&

Llamado

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

> <>, 74 bytes

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

Esta solución requiere que la cadena esté truncada y Lque ya esté en la pila, en ese orden.

Hay 7 bytes desperdiciados causados ​​por problemas de alineación, que todavía intentan eliminarlos.

Sok
fuente
1

C # (157):

Basado en la respuesta de Salah Alami , pero más corto. La clase de cadena se deriva de IEnumerable<char>, por lo que en lugar de T.Contains(" ")||T.Contains("-"), yo uso " -".Any(x=>T.Contains(x)).

Solución:

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)+"...";}

Sin 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) + "...";
}

Actualizar:

Guardado 6 bytes gracias al comentario de SLuck49, utilizando en Any(T.Contains)lugar de Any(x=>T.Contains(x)).

Abbas
fuente
1
Para .Any(x=>T.Contains(x))que pueda utilizar directamente el método en lugar de un lambda como .Any(T.Contains)para guardar 6 bytes
SLuck49
@ SLuck49 gracias, actualicé mi respuesta.
Abbas
1

GS2 , 29 bytes

Este programa toma entrada estándar. La primera línea es la cadena y la segunda es el número de longitud objetivo.

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

El código GS2 puede ser un poco difícil de leer a veces. :) Aquí hay algunos comentarios.

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
recursivo
fuente
1

Groovy, 56 bytes

Copié la respuesta de Kleyguerth primero, de ahí los mismos nombres de variables ...

Recorte la cadena hacia abajo por 2 caracteres, luego la mayoría de los trabajos se realiza mediante la expresión regular, reemplace un guión o un espacio seguido de cualquier número de caracteres que no sean un guión o un espacio al final de la cadena con un "." O reemplace cualquier carácter al final de la cadena si todos los caracteres anteriores no son ni un guión ni un espacio con un ".". Más difícil de poner en palabras que escribir la expresión regular ...

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

Editar: en realidad, puede eliminar la parte de la cadena que coincide con la expresión regular y agregar "..." al final:

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

Limpio , 89 bytes

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

Pruébalo en línea!

Como una función $ :: Int String -> String

Οurous
fuente
0

C # (compilador interactivo de Visual C #) , 117 bytes

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

Basado en @ Abba's, que se basa en la respuesta de @Salah Alami. En lugar de usar Containsuna Substringllamada innecesaria , usa IndexOf para verificar si existe un guión o espacio en la cadena truncada.

Pruébalo en línea!

Encarnación de la ignorancia
fuente