Montag Minigolf # 2: Langtext abschneiden

25

Montag Minigolf: Eine Reihe von kurzen Herausforderungen, die (hoffentlich!) Jeden Montag veröffentlicht werden.

Viele Webanwendungen (insbesondere soziale Medien) kürzen lange Textpassagen automatisch, damit sie in die Formatierung der App passen. In dieser Herausforderung werden wir einen Algorithmus erstellen, mit dem eine Textpassage automatisch auf eine bestimmte Länge gekürzt wird.

Herausforderung

Das Ziel der Herausforderung besteht darin, ein Programm oder eine Funktion zu schreiben, die zwei Argumente enthält:

  • T , der Text, der abgeschnitten werden soll.
  • L , die maximale Länge, die zurückgegeben werden soll.

Und gibt T zurück , abgeschnitten mit der folgenden Logik:

  • Wenn die Länge von T kleiner oder gleich L ist , ist keine Kürzung erforderlich. Geben Sie die ursprüngliche Zeichenfolge zurück.
  • Kürzen Sie T auf die Länge L -2. Wenn dies keine Leerzeichen oder Bindestriche enthält, geben Sie T mit genau L -3 Zeichen gefolgt von einem Auslassungszeichen zurück ....
  • Ansonsten kürzen Sie das Ende des Ergebnisses bis zum letzten Leerzeichen oder Bindestrich. Fügen Sie ein Auslassungszeichen hinzu ...und geben Sie das Ergebnis zurück.

Einzelheiten

  • T und L können in beliebiger Reihenfolge und in beliebigem Format angegeben werden.
  • Sie können annehmen, dass 3 < L <2 31 .
  • Sie dürfen U + 2026 Horizontal Ellipsis nicht verwenden . Sie müssen drei Punkte verwenden.
  • Die Eingabe beginnt nicht mit einem Leerzeichen oder einem Bindestrich.
  • Die Eingabe enthält keine anderen Leerzeichen als normale Leerzeichen. (Keine Tabulatoren, Zeilenumbrüche usw.)

Testfälle

Eingänge:

"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

Ausgänge:

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

(Beachten Sie, dass die Anführungszeichen nur angeben sollen, dass es sich um Zeichenfolgen handelt. Sie müssen nicht eingeschlossen werden.)

Wertung

Das ist , also gewinnt der kürzeste gültige Code in Bytes. Tiebreaker geht zu einer Übermittlung über, die als erste ihre letzte Bytezahl erreicht hat. Der Gewinner wird am kommenden Montag, 5. Oktober, ermittelt. Viel Glück!

Bearbeiten: Herzlichen Glückwunsch an Ihren Gewinner, @Jakube mit Pyth erneut, mit 25 Bytes!

ETHproductions
quelle
7
Antworten auf diese Herausforderung sollten standardmäßig in der jeweiligen Sprache verfügbar sein. Zu oft habe ich UI gesehen, die schlechte Trunca ...
Sanchises
1
... "Ansonsten schneiden Sie das Ende des Ergebnisses bis zu und" NICHT "einschließlich des letzten Leerzeichens oder Bindestrichs." Recht?
Anatolyg
Wird der Text Tabs haben?
kirbyfan64sos
@anatolyg Nein, da dann das letzte Leerzeichen oder der Bindestrich vor der Ellipse angezeigt wird.
ETHproductions
@ kirbyfan64sos Nein. Ich werde das zum Abschnitt Details hinzufügen.
ETHproductions

Antworten:

12

Pyth, 25 Bytes

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

Probieren Sie es online aus: Demo oder Test Suite

Erläuterung:

+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
quelle
4
Ich mag die Art und Weise, wie der Code am Ende
nachlässt
7

Perl, 69 59 52 Bytes

51 Byte Code + 1 Byte Befehlszeile. Angenommen, die numerische Eingabe darf mit dem Parameter -i erfolgen.

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

Verwendung:

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

Python 2, 78 73 Bytes

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

Das Eingabeformat folgt der Beispieleingabe.

xsot
quelle
1
Ein bekannter Name aus Anarchy Golf. Herzlich willkommen!
29.
7

JavaScript (ES6), 123 78 67 61 Bytes

Ich hatte nicht erwartet, dass ich das so stark reduzieren könnte, aber es stellte sich heraus, dass die Kombination aus Spleißen und Ersetzen jeden Fall abdecken kann, in dem eine Kürzung erforderlich ist.

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

Das erste Argument ist die Zeichenfolge, das zweite die Länge. Besonderer Dank geht an edc65 für die Längenüberprüfungsoptimierung!

Hier ist der Originalcode (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
quelle
4
Klug! +1. Tipp: Oft muss .lengthdie Länge einer Saite nicht überprüft werden. (T,L)=>T[L]?T.slice(0,L-2).replace(/([ -][^ -]*|.)$/,'...'):TPunktzahl 61
edc65
@ edc65 Doh! Ich hatte nach einer Optimierung für die Längenprüfung gesucht und gedacht, es müsste eine Möglichkeit geben, diese zu reduzieren, aber Ihre Methode war mir nicht in den Sinn gekommen. Hervorragender Vorschlag! : D
Mwr247
Sie können ersetzen [ -][^ -]mit \s\Szu speichern 5 mehr Bytes
Shaun H
Tolle Lösung! @ShaunH, wenn er das tut, funktioniert es sicher nicht für die Bindestriche?
Jarmex,
@Jarmex Dumme Hirn, ja, das tut es definitiv nicht.
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 verfügt nicht über viele Befehle zur Manipulation von Zeichenfolgen. Daher müssen wir den letzten Index manuell suchen: Wenn die Zeichenfolge die zu suchende Zeichenfolge nicht enthält, wird inString(0 zurückgegeben. Wir suchen nach Bindestrichen und Leerzeichen, beginnend an jeder Position von 1 bis Lund notieren Sie die größte Zahl, die kleiner oder gleich ist L-3. Wenn diese Zahl Iimmer noch 0 ist, verwenden wir L-3stattdessen als Endungsindex.

Aufgrund der Einschränkungen des Rechners beträgt der größte adressierbare Index eines Strings 9999; Daher schlägt dies für größere Zeichenfolgen fehl.

Ich verlasse mich auf das Verhalten des Rechners, die Variable automatisch Iauf 0 zu initialisieren. Löschen Ioder löschen Sie daher den Speicher Ihres Rechners, bevor Sie ihn ausführen.

Lirtosiast
quelle
Es gibt eine kürzere Lösung, die Listen verwendet, um den größten Index zu finden, aber dann wäre die Größenbeschränkung ~ 500, nicht
9999.
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
quelle
Ja, ich habe nur Leerzeichen entfernt, um die Anzahl der Bytes zu verringern.
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]+'...'

Angerufen mit

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

Ziemlich unkompliziert, kann wohl weiter golfen werden

Kleyguerth
quelle
1

CJam, 34 Bytes

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

Probieren Sie es online aus: Chrome | Feuerfuchs

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

Verwendung:

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

Sam cd
quelle
1

rs , 116

(\d+)$/(_)^^(\1)
___$/
\t
+\t(.)(.*) (_)(_*)$/\1\t\2 \4
\t(.*?)_+$/\1\t
\t( ?).+/\1
[- ][^- \t]*$/
(?<!\t)$/...
 \t/

Zumindest ist es kürzer als C # ...

Live-Demo und Testfälle.

kirbyfan64sos
quelle
1

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

Ungolfed Original:

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

Dies sind 386 Bytes / Zeichen. Einige interessante Features hier:

Die x[y:z]Syntax ist syntaktischer Zucker für x.measure(y, z)und gibt einen Unterbereich von xbeginnend ymit Länge zurück z- für Zeichenfolgen ist dies eine Teilzeichenfolge. (Es gibt auch x[y..z]Syntax, die eine ist Spanne von Index y bis z, beide eingeschlossen, wie auch halboffene Spannweiten x[...z]und x[y...].)

List.lastIndexWhereNimmt ein Prädikat (dh eine Funktion, die ein Listenelement nimmt und einen Booleschen Wert zurückgibt, dh hier a Callable<Boolean, [Character]>) und gibt den Index des letzten Listenelements an, in dem das Prädikat erfüllt ist (oder null, wenn es nie erfüllt ist). Da Strings Listen sind, funktioniert dies auch für Strings.

Das Ergebnis spaceIndexist vom Typ Integer|Nulloder Integer?kurz - dh es kann entweder eine Ganzzahl oder null(der einzige Wert von Typ Null) sein. (Der Name spaceIndexstammt aus der Zeit, als ich nicht wusste, dass -das auch etwas Besonderes ist - ich denke, es breakIndexwäre besser.)

Mit können exists spaceIndexwir überprüfen, ob spaceIndexnicht null ist, und dann etwas anderes tun. (In diesem if-Block weiß der Compiler, dass er nicht null ist ... ohne dass er sich beschwert hätte, wenn ich spaceIndexauf den String zugegriffen hätte.)

Anstelle der lokalen Funktion können spacePredicatewir auch eine anonyme Funktion verwenden

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

Dies bringt uns auf 333 Zeichen:

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

Die nächste Optimierung besteht darin, kürzere Variablen- und Funktionsnamen zu verwenden, was uns um 81 Bytes auf 252 reduziert:

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

Für die Prädikatfunktion muss tatsächlich kein Argumenttyp deklariert sein, der vom Compiler abgeleitet werden kann. Gleiches gilt für die Art von i(wo wir noch schreiben müssen value, um es als Deklaration zu kennzeichnen). Diese Erklärung ist jetzt kurz genug, um in eine Zeile zu passen, und bringt uns auf 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]+"...";
}

Anstelle von können e == ' ' || e == '-'wir auch schreiben e in [' ', '-'](oder e in {' ', '-'}, dies ist ein iterierbarer Konstruktor anstelle eines Tupels). Der inOperator ordnet der Methode Category.contains zu, was uns auf die Idee bringt, dass wir die containsMethode dieses Tupels direkt übergeben können (es ist ein aufrufbares Objekt, das ein beliebiges Objekt annimmt, also auch Zeichen akzeptiert), ohne das (e) => ...Boilerplate (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]+"...";
}

Eine andere Kategorie, die dieselben zwei Zeichen enthält, ist die aus zwei Zeichen bestehende Zeichenfolge " -". (Außerdem enthält es auch seine Teilzeichenfolgen, aber das tut hier nicht weh). 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]+"...";
}

Ich denke, wir haben das Beste aus dieser Zeile herausgeholt, wenden wir uns den anderen zu ... Die letzten beiden return-Anweisungen haben eine Ähnlichkeit, die wir ausnutzen können - sie unterscheiden sich nur in ivs. l-3und verwenden inur, wenn sie nicht null sind l-3. Zum Glück ist das genau das, wofür der elseBediener gemacht ist!

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

(Die Klammern scheinen hier erforderlich zu sein, da elsesie eine niedrigere Priorität haben als [:].) Dies sind 171 Zeichen. Jetzt iwird es nur einmal verwendet, damit wir es einbinden können, sodass wir 153 Zeichen haben:

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

Wir können diese if-return-returnKombination auch durch eine Kombination der Operatoren thenund elsein einem ersetzen return. ( thenGibt den zweiten Operanden zurück, wenn der erste wahr ist, andernfalls null, wodurch elseder zweite Operand zurückgegeben werden kann.) 131 Bytes (obwohl einige der Einsparungen die Leerzeichen sind, die wir sowieso beseitigen werden):

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

Eine Funktion, die nur einen Rücksprung mit einem Ausdruck enthält, kann alternativ mit der "Fettpfeil" -Notation geschrieben werden, die 123 ergibt:

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

Durch Entfernen des nicht benötigten Leerzeichens erhalten wir die letzten 111 Bytes:

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

Zusätzlich gibt es hier eine Funktion, die die Beispiele aus der Frage druckt (unter Verwendung des Namens, tder nach Schritt zwei verwendet wird):

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

POSIX-Shell + GNU sed, 65 Bytes

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

Dies ist ein Job für sed gemacht! Aber ich brauchte Shell, um das Längenlimit zu erreichen (vielleicht wäre Perl besser). Der sed-Teil erweitert sich zu einer ziemlich einfachen Sequenz mit bedingten Sprüngen, wenn wir fertig sind:

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

Genannt als

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

> <> 74 Bytes

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

Bei dieser Lösung muss die Zeichenfolge Lin dieser Reihenfolge abgeschnitten sein und sich bereits auf dem Stapel befinden.

Es gibt 7 verschwendete Bytes, die durch Ausrichtungsprobleme verursacht wurden und immer noch versuchen, diese auszuspielen.

Sok
quelle
1

C # (157):

Basierend auf der Antwort von Salah Alami , aber kürzer. Die Zeichenfolgenklasse leitet sich von ab IEnumerable<char>, also verwende T.Contains(" ")||T.Contains("-")ich stattdessen " -".Any(x=>T.Contains(x)).

Lösung:

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

Aktualisieren:

Gespeichert 6 Bytes dank des Kommentars von SLuck49, mit Any(T.Contains)anstelle von Any(x=>T.Contains(x)).

Abbas
quelle
1
Denn .Any(x=>T.Contains(x))Sie können direkt die Methode anstelle eines Lambda verwenden .Any(T.Contains), um 6 Bytes zu sparen
SLuck49
@ SLuck49 danke, aktualisiert meine Antwort.
Abbas
1

GS2 , 29 Bytes

Dieses Programm verwendet die Standardeingabe. Die erste Zeile ist die Zeichenfolge, und die zweite Zeile ist die Ziellängennummer.

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

GS2-Code ist manchmal etwas schwer zu lesen. :) Hier ist ein Kommentar.

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
rekursiv
quelle
1

Groovy, 56 Bytes

Kopierte zuerst die Antwort von Kleyguerth, daher die gleichen Variablennamen ...

Kürzen Sie die Zeichenfolge um 2 Zeichen. Die meiste Arbeit erledigt dann der Regex. Ersetzen Sie einen Bindestrich oder ein Leerzeichen, gefolgt von einer beliebigen Anzahl von Zeichen, die kein Bindestrich oder Leerzeichen am Ende der Zeichenfolge sind, durch ein "." ODER Ersetzen Sie ein beliebiges Zeichen am Ende der Zeichenfolge, wenn alle Zeichen davor weder ein Bindestrich noch ein Leerzeichen mit einem "." Schwieriger in Worte zu fassen als den Regex zu schreiben ...

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

Bearbeiten: Eigentlich kann man einfach den Teil der Zeichenkette entfernen, der mit der Regex übereinstimmt und am Ende "..." hinzufügen:

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

Sauber , 89 Bytes

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

Probieren Sie es online!

Als eine Funktion $ :: Int String -> String

Οurous
quelle
0

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

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

Basierend auf @ Abba's, basierend auf der Antwort von @ Salah Alami. Anstatt Containsund unnötig zu verwendenSubstring Aufruf zu verwenden, wird IndexOf verwendet, um zu überprüfen, ob in der abgeschnittenen Zeichenfolge ein Bindestrich oder ein Leerzeichen vorhanden ist.

Probieren Sie es online!

Verkörperung der Ignoranz
quelle