Segunda-feira Mini-Golfe # 2: Truncando texto longo

25

Mini-golfe de segunda-feira: Uma série de desafios curtos , publicados (espero!) Toda segunda-feira.

Muitos aplicativos da Web (especialmente as mídias sociais) truncam automaticamente longas passagens de texto para que se ajustem à formatação do aplicativo. Neste desafio, criaremos um algoritmo para aparar automaticamente uma passagem de texto em um determinado comprimento.

Desafio

O objetivo do desafio é escrever um programa ou função que aceite dois argumentos:

  • T , o texto a ser truncado.
  • L , o comprimento máximo para retornar.

E retorna T , truncado com a seguinte lógica:

  • Se o comprimento de T for menor ou igual a L , nenhum truncamento será necessário. Retorne a string original.
  • Trunque T no comprimento L -2. Se isso não contiver espaços ou hífens, retorne T truncado para exatamente L -3 caracteres, seguido por reticências ....
  • Caso contrário, apare o final do resultado até o último espaço ou hífen. Adicione uma elipse ...e retorne o resultado.

Detalhes

  • T e L podem ser obtidos em qualquer ordem e em qualquer formato.
  • Você pode assumir que 3 < L <2 31 .
  • Você não pode usar reticências horizontais U + 2026 ; você deve usar três períodos.
  • A entrada não começará com um espaço ou um hífen.
  • A entrada não conterá nenhum espaço em branco que não seja espaços regulares. (Sem guias, novas linhas etc.)

Casos de teste

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

Saídas:

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

(Observe que as aspas são apenas para especificar que são strings; elas não precisam ser incluídas.)

Pontuação

Este é o , pelo que o código válido mais curto em bytes vence. O desempatador vai para o envio que atingiu sua contagem final de bytes primeiro. O vencedor será escolhido na próxima segunda-feira, 5 de outubro. Boa sorte!

Edit: Parabéns ao seu vencedor, @Jakube with Pyth novamente, com 25 bytes!

ETHproductions
fonte
7
As respostas para esse desafio devem ser um recurso padrão em seus respectivos idiomas. Muitas vezes eu vi interface do usuário que apresentam más trunca ...
Sanchises
1
... "Caso contrário, apare o final do resultado em e" NÃO ", incluindo o último espaço ou hífen." Direita?
Anatolyg 29/09/2015
O texto terá guias?
Kirbyfan64sos
@anatolyg Não, porque o espaço ou hífen final apareceria antes das reticências.
ETHproductions
@ kirbyfan64sos Não. Vou adicionar isso à seção Detalhes.
ETHproductions

Respostas:

12

Pitão, 25 bytes

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

Experimente on-line: Demonstration or Test Suite

Explicação:

+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
fonte
4
Eu gosto da forma como o código trilhas off no final ...
mathmandan
7

Perl, 69 59 52 bytes

Código de 51 bytes + linha de comando de 1 byte. Assume que é permitido que a entrada numérica seja fornecida com o 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
fonte
7

Python 2, 78 73 bytes

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

O formato de entrada segue o exemplo de entrada.

xsot
fonte
1
Um nome familiar da Anarchy Golf. Bem vinda!
xnor
7

JavaScript (ES6), 123 78 67 61 bytes

Eu não esperava poder reduzir tanto isso, mas acontece que a combinação de emenda / substituição é capaz de cobrir todos os casos em que o truncamento é necessário.

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

O primeiro argumento é a string, o segundo é o comprimento. Agradecimentos especiais a edc65 pela otimização da verificação de comprimento!

Aqui está o 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
fonte
4
Inteligente! +1. Dica: muitas vezes você não precisa .lengthpara verificar o comprimento de uma string (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):Tpontuação 61
edc65
@ edc65 Doh! Eu estava procurando uma otimização na verificação de comprimento, pensando que tinha que haver uma maneira de reduzir isso, mas seu método não havia me ocorrido. Excelente sugestão! : D
Mwr247 29/09/2015
Você pode substituir [ -][^ -]com \s\Smais para salvar 5 bytes
Shaun H
Ótima solução! @ ShaunH, se ele fizer isso, não funcionará para os hífens, com certeza?
Jarmex 29/09/2015
@ Jarmex Cérebro tolo, sim, definitivamente não.
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

O TI-BASIC não possui muitos comandos de manipulação de string, portanto, precisamos encontrar o último índice manualmente: se a string não contiver a string a ser pesquisada, inString(retornará 0. Procuramos hífens e espaços começando em todas as posições de 1 para Le registre o maior número menor ou igual a L-3. Se esse número Iainda for 0, usamos L-3o índice final.

Devido às limitações da calculadora, o maior índice endereçável de uma string é 9999; portanto, isso falhará para cadeias maiores.

Eu confio no comportamento da calculadora de inicializar automaticamente a variável Ipara 0, então exclua Iou limpe a memória da calculadora antes de executar.

lirtosiast
fonte
Há uma solução mais curto usando listas para encontrar o maior índice, mas, em seguida, o limite de tamanho seria ~ 500, não 9999.
lirtosiast
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
fonte
sim, é claro, acabei de remover espaços para diminuir 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]+'...'

Chamado com

>>> print t("This is some very long text.", 25)
This is some very long...
Celeo
fonte
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]+'...'}

Bem simples, provavelmente pode ser jogado mais longe

Kleyguerth
fonte
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
fonte
1

Ceilão 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 Não Goleado:

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

São 386 bytes / caracteres. Algumas características interessantes aqui:

A x[y:z]sintaxe é açúcar sintático para x.measure(y, z)e retorna um subintervalo de xinício ycom comprimento z- para seqüências de caracteres, este é um substring. (Também há x[y..z]sintaxe, que é uma extensão do índice y até z, tanto inclusivas quanto também com aberturas pela metade x[...z]e x[y...].)

List.lastIndexWhere pega um predicado (ou seja, uma função que pega um elemento da lista e retorna um booleano, ou seja, aqui Callable<Boolean, [Character]> ) e fornece o índice do último elemento da lista em que o predicado é atendido (ou nulo, se nunca for atendido). Como strings são listas, isso também funciona para strings.

O resultado disso spaceIndexé do tipo Integer|Nullou, Integer?para abreviar - ou seja, pode ser um número inteiro ou null(o único valor do tipo Null). (O nome spaceIndexvem de quando eu não percebi que isso -também era especial - acho breakIndexque seria melhor.)

Com exists spaceIndexpodemos verificar se spaceIndexnão é nulo e, então, fazer algo diferente. (Dentro deste bloco if, o compilador sabe que não é nulo ... sem isso, teria reclamado se eu usassespaceIndex acesso à string.)

Em vez da função local spacePredicate, também podemos usar uma função anônima

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

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

A próxima otimização é usar nomes mais curtos de variáveis ​​e funções, o que nos reduz em 81 bytes para 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]+"...";
}

A função predicado, na verdade, não precisa do seu tipo de argumento declarado, que pode ser inferido pelo compilador. O mesmo para o tipo de i(onde ainda precisamos escrever valuepara marcá-lo como uma declaração). Agora essa declaração é curta o suficiente para caber em uma linha, reduzindo-nos 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]+"...";
}

Em vez de e == ' ' || e == '-'também podemos escrever e in [' ', '-'](ou e in {' ', '-'}, este é um construtor iterável em vez de um tuplo). O inoperador mapeia para o método Category.contains, o que nos leva à idéia de que podemos passar o containsmétodo dessa tupla diretamente (é possível chamar qualquer objeto, aceitando também o caractere), sem o (e) => ...clichê (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]+"...";
}

Na verdade, outra categoria que contém os mesmos dois caracteres é a sequência de dois caracteres " -". (Além disso, ele também contém suas substrings, mas isso não dói aqui). 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]+"...";
}

Acho que tiramos o máximo proveito dessa linha, vamos nos voltar para as outras ... as duas últimas declarações de retorno têm alguma semelhança que podemos explorar - elas diferem em ivs. l-3e estão usando iexatamente quando não é nulo, caso contrário l-3. Felizmente, é exatamente para isso que o elseoperador é feito!

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

(Os parênteses parecem ser necessários aqui, pois elsetêm uma precedência mais baixa que [:].) São 171 caracteres. Agora ié usado apenas uma vez, para que possamos incorporá-lo, levando-nos 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)] + "...";
}

Também podemos substituir essa if-return-returncombinação por uma combinação dos operadores thene elseem um return. ( thenretorna é o segundo operando quando o primeiro é verdadeiro, caso contrário, nulo, o que permite elseretornar seu segundo operando.) 131 bytes (embora algumas das economias sejam os espaços em branco que serão eliminados de qualquer maneira):

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

Uma função que contém apenas um retorno com uma expressão pode, alternativamente, ser escrita com a notação "flecha gorda", fornecendo 123:

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

A remoção do espaço em branco desnecessário nos dá os 111 bytes finais:

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

Além disso, aqui está uma função que imprime os exemplos da pergunta (usando o nome tque é usado após a etapa dois):

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

Shell POSIX + GNU sed, 65 bytes

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

Este é um trabalho feito para sed! Mas eu precisava do shell para obter o limite de comprimento (talvez o Perl fosse melhor). A parte sed se expande para uma sequência bastante simples, com saltos condicionais quando terminamos:

s/(.{$1}).+/\1/
T
s/(.*)[- ]...*/\1.../
t
s/...$/.../
:
Toby Speight
fonte
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]]&

Chamado como

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

> <>, 74 bytes

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

Essa solução requer que a sequência esteja truncada e Ljá esteja na pilha, nessa ordem.

Há 7 bytes desperdiçados causados ​​por problemas de alinhamento, ainda tentando resolver esses problemas.

Sok
fonte
1

C # (157):

Com base na resposta de Salah Alami , mas mais curto. A classe string deriva de IEnumerable<char>, então, em vez de T.Contains(" ")||T.Contains("-"), eu uso" -".Any(x=>T.Contains(x)) .

Solução:

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

Ungolfed:

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

Atualizar:

Economizou 6 bytes graças ao comentário de SLuck49, usando em Any(T.Contains)vez de Any(x=>T.Contains(x)).

Abbas
fonte
1
Para .Any(x=>T.Contains(x))que você pode usar diretamente o método em vez de um lambda como .Any(T.Contains)para salvar 6 bytes
SLuck49
@ SLuck49 obrigado, atualizei minha resposta.
Abbas
1

GS2 , 29 bytes

Este programa recebe entrada padrão. A primeira linha é a sequência e a segunda é o número do comprimento alvo.

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

Às vezes, o código GS2 pode ser um pouco difícil de ler. :) Aqui estão alguns comentários.

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

Groovy, 56 bytes

Copiei a resposta de Kleyguerth primeiro, daí os mesmos nomes de variáveis ​​...

Apare a string em 2 caracteres, e a maior parte do trabalho é feita pela regex, substitua um traço ou um espaço seguido por qualquer número de caracteres que não sejam um traço ou um espaço no final da string com um "." OU substitua qualquer caractere no final da string se todos os caracteres anteriores a ela não forem um traço ou um espaço com um ".". Mais difícil de colocar em palavras do que escrever o regex ...

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

Edit: Na verdade, basta remover a parte da string que corresponde ao regex e adicionar "..." no final:

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

Limpo , 89 bytes

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

Experimente online!

Como uma função $ :: Int String -> String

Furioso
fonte
0

C # (compilador interativo do 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

Baseado no @ Abba's, baseado na resposta do @Salah Alami. Em vez de usar Containse um desnecessárioSubstring chamada , ele usa IndexOf para verificar se existe um hífen ou espaço na cadeia truncada.

Experimente online!

Modalidade de ignorância
fonte