nur das letzte Vorkommen eines Strings entfernen
Vb1988
- javascript
Hallo,
"user hashimoto has two dogs"
wie kann man das letzte Vorkommen von "has" entfernen? (Problem: "hashimoto" beinhaltet ebenfalls das Wort "has". Die replace() Funktion kann deshalb nicht angewendet werden)
Du kannst mit lastIndexOf die Position des letzten Vorkommens bestimmen und dann mit substr bzw. substring Operationen herausschneiden.
Rolf
Hallo Vb1988,
"user hashimoto has two dogs"
wie kann man das letzte Vorkommen von "has" entfernen? (Problem: "hashimoto" beinhaltet ebenfalls das Wort "has". Die replace() Funktion kann deshalb nicht angewendet werden)
Reguläre Ausdrücke sind auch nicht notwendig, wenn du lediglich Teilstrings suchst.
var original = "user hashimoto has two dogs";
var pos = original.lastIndexOf("has");
var neu = original.substr(0,pos) + original.substr(pos+3);
Bis demnächst
Matthias
statt:
var original = "user hashimoto has two dogs";
var pos = original.lastIndexOf("has");
var neu = original.substr(0,pos) + original.substr(pos+3);
muss es lauten:
var original = "user hashimoto has two dogs";
var pos = original.lastIndexOf("has");
var neu = original.substr(0,pos) + original.substr(pos+1);
Hallo Vb1988,
Ich hab dir gern geholfen.
statt:
var neu = original.substr(0,pos) + original.substr(pos+3);
muss es lauten:
var neu = original.substr(0,pos) + original.substr(pos+1);
Warum?
Bis demnächst
Matthias
Hello,
"user hashimoto has two dogs"
wie kann man das letzte Vorkommen von "has" entfernen? (Problem: "hashimoto" beinhaltet ebenfalls das Wort "has". Die replace() Funktion kann deshalb nicht angewendet werden)
Du könntest mit String.split() ein Array daraus machen und das dann wieder mit dem Kleber zusammenkleben zum String. Vor dem letzten Element lässt Du den Kleber einfach weg.
Liebe Grüße
Tom S.
Hello,
was gibt's denn da zu bemängeln an der Idee?
Siehe Wiki!
Liebe Grüße
Tom S.
Ich hab's nicht runtergewertet, aber beim ersten Blick drauf kommt mir die Methode recht umständlich vor. Beim zweiten Blick dagegen... "Vor dem letzten Element den Kleber weglassen" ist knifflig, die join-Methode ist darauf nicht ausgelegt. Es braucht also noch etwas Trixerei.
var original = "ghastly user hashimoto hastes to tell us that he has two dogs";
var parts = original.split('has');
// Ergibt ['g', 'tly user ', 'himoto ', 'tes to tell us that he ', ' two dogs']
var lastpart = parts.pop();
var result = parts.join('has') + lastpart;
// --> "ghastly user hashimoto hastes to tell us that he two dogs"
Tatsächlich, sieht gar nicht so wüst aus - aber ein split+join ist vom Gefühl her aufwändiger als ein lastIndexOf und etwas substr-Gebastel.
Abgesehen davon müsste uns der OP noch sagen, ob diese Logik (ob nun mit split/pop/join oder mit lastIndexOf+substr) hinreichend ist oder ob noch eine Doppelleerzeichensanierung nötig ist.
Rolf
Hello,
aber Javascript hat doch auch noch For-Schleifen, oder wurden die abgeschafft? :-p
Liebe Grüße
Tom S.
Hm, ich bin vermutlich zu sehr in C# sozialisiert, wo eine Serien von Stringverkettungen ineffizient ist. Deshalb habe ich gerade nach einem Gegenstück zum StringBuilder gesucht (oder StringBuffer auf javanesisch), den es in JavaScript wohl nicht gibt. Der Tenor ist aber, dass eine Folge von Stringverkettungen in Javascript nicht die gleiche Performancestrafe hat wie in C# oder Java.
Rolf
Hallo,
was gibt's denn da zu bemängeln an der Idee?
vermutlich ist die Idee nicht nachvollziehbar formuliert gewesen, auch ich habe sie erst mit Rolfs Beispiel begriffen.
Gruß
Kalk
@@TS
was gibt's denn da zu bemängeln an der Idee?
Dass split()
hier nicht Mittel der Wahl ist, sondern lastIndexOf()
– wie von Rolf und Matthias gesagt.
LLAP 🖖
Hello,
warum?
Was wäre, wenn wir nicht das letzte Vorkommen, sondern das n-te behandeln wollten?
Liebe Grüße
Tom S.
@@TS
Was wäre, wenn wir nicht das letzte Vorkommen, sondern das n-te behandeln wollten?
Dann würden wir indexOf()
verwenden.
LLAP 🖖
Hallo TS,
Was wäre, wenn wir nicht das letzte Vorkommen, sondern das n-te behandeln wollten?
Das wird mit pop und join wohl wesentlich aufwändiger. Bei der anderen Lösung ändert sich praktisch nur der Funktionsaufruf. Aus lastIndexOf
wird indexOf
mit einem n
als Parameter.
Bis demnächst
Matthias
Hello,
ich habe nicht von pop und join gesprochen, sondern von .split() und for()
mit .indexOf() wird man wohl mehrere Stufen bebötigen, um sich bis zum n-ten Vorkommen vorzuhangeln? Das erscheint mir dann schnell unübersichtlich und anfällig für die berühmten n+1-Fehler.
Nachher mehr vom Desktop.
Liebe Grüße
Tom S.
Hello,
so, wie versprochen, meine Version:
Ersetze das n-te Vorkommen des Suchstrings im Input-String gegen den Ersetzungsstring, wenn occur==0, ersetze alle Vorkommen (was dann dem Replace entsprechen würde).
Da mag nun jeder selbst entscheiden, wie er das für sich selber noch modifizieren mag oder doch lieber die indexOf()-Methode benutzen mag. Die haben wir ja noch nicht fertig gesehen.
Für die Zählung des Vorkommens von hinten könnte man dann z. B. occur auch negativ einbauen. Vielleicht hat ja jemand Lust, mal zu überlegen, wie man das am effektivsten einbauen würde, also ohne doppelten Code zu schreiben 😉
function replace_nth(input, search, replace, occur)
{
input = '|' + input + '|';
var items = input.split(search);
var result = '';
for(n = 0; n < items.length -1; n++)
{
if (occur == 0 || n == occur - 1)
{
result += items[n] + replace;
}
else
{
result += items[n] + search;
}
}
result += items[items.length-1];
result = result.substring(1, result.length-1);
return result;
}
oder auf getscript.de
Liebe Grüße
Tom S.
Hello,
und nochmal neu mit "occur darf auch negativ sein", dann wird von hinten gezählt.
function replace_nth(input, search, replace, occur)
{
occur = parseInt(occur);
input = '|' + input + '|';
var items = input.split(search);
var result = '';
for(n = 0; n < items.length -1; n++)
{
if (occur == 0 || n == occur - 1)
{
result += items[n] + replace;
}
else if ((occur < 0) && (n == items.length - 1 + occur))
{
result += items[n] + replace;
}
else
{
result += items[n] + search;
}
}
result += items[items.length-1];
result = result.substring(1, result.length-1);
return result;
}
Kleine Nebenerkenntnis: mit positiven Werten geht es ohne parseInt(), mit negativen aber nicht. Kann mir das jemand erklären?
Ergebnis auf getscript.de
Liebe Grüße
Tom S.
Tach!
function replace_nth(input, search, replace, occur) { occur = parseInt(occur);
Es ist nicht Aufgabe der Funktion, eine Eingabedatenkorrektur vorzunehmen. Stattdessen solltest du die Werte da in das richtige Format umwandeln, wo du sie aus ihrer Quelle holst.
Kleine Nebenerkenntnis: mit positiven Werten geht es ohne parseInt(), mit negativen aber nicht. Kann mir das jemand erklären?
Ich grad nicht, aber Strings sind eben keine Zahlen. Und Javascript ist auch nicht ganz so großzügig wie PHP bei Strings, die Zahlen enthalten.
dedlfix.
Hello,
function replace_nth(input, search, replace, occur) { occur = parseInt(occur);
Es ist nicht Aufgabe der Funktion, eine Eingabedatenkorrektur vorzunehmen. Stattdessen solltest du die Werte da in das richtige Format umwandeln, wo du sie aus ihrer Quelle holst.
Ich mag eigentlich eigensichere Programmierung, wenn es möglich ist. Die Funktionen überprüfen ihre Argumente, sanieren sie, wenn sinnvoll oder brechen ab, wenn Fehler oder Fehlanzeigen vorhanden sind.
Dazu gehört auch, dass Werte solange im Urzustand erhalten bleiben, bis sie benötigt werden. Man weiß ja nie, mit welchen Funktionen/Methoden man sonst noch darauf zugreifen muss.
Aber das ist meine persönliche Philosphie.
Liebe Grüße
Tom S.
Tach!
Ich mag eigentlich eigensichere Programmierung, wenn es möglich ist. Die Funktionen überprüfen ihre Argumente, sanieren sie, wenn sinnvoll oder brechen ab, wenn Fehler oder Fehlanzeigen vorhanden sind.
Dazu gehört auch, dass Werte solange im Urzustand erhalten bleiben, bis sie benötigt werden. Man weiß ja nie, mit welchen Funktionen/Methoden man sonst noch darauf zugreifen muss.
Ich weiß ja nicht, wozu du den Eingabewert als String noch benötigst, aber auch dann sollte die API einer Funktion klar definiert sein und nicht als "übergib mir einfach irgendwas, ich korrigiere mir das so zu recht, wie ich denke, dass es richtig ist".
Wenn es unbedingt eine Sicherung sein soll, teste auf Number ungleich NaN oder ähnliches und brich ab, statt zu mutmaßen, dass parseInt() die richtige Funktion ist, um den Eingaberwert zu korrigieren. Das wäre auch eher Eigenmächtigkeit statt Eigensicherung. So wie das jetzt ist, sichert das jedenfalls nur zahlenähnliche Eingaben. Alles andere ergibt NaN und macht das Arbeiten im Inneren der Funktion ineffektiv. (Nebenbei: effektiv und effizient sind keine Synonyme.)
Wenn du darauf bestehst, einen String bis an die Funktion führen zu wollen, dann schreib das parseInt() lieber in die Parameterübergabe beim Aufruf der Funktion.
Aber das ist meine persönliche Philosphie.
Die Anzahl der Vorkommen sollte eine Zahl sein. Und wenn dazu eine Hilfsvariable angelegt werden muss, um deiner Philosophie gerecht zu werden. Alternativ kann der Wert erneut aus dem Eingabefeld abgefragt werden, wenn er unbedingt im Original inklusive aller Eingabefehler benötigt wird.
dedlfix.
Die Anzahl der Vorkommen sollte eine Zahl sein.
Das sehe ich auch so, ein weiteres Problem eines stillen Fehlschlags ist, dass offensichtliche Programmierfehler sich in die Anwendung schleichen können, weil die Überprüfung erst zur Laufzeit stattfindet.
Ein statischer Typchecker deckt solche Fehler bereits während des Tippens auf:
Meine bisherige Erfahrung mit APIs ist eigentlich auch die, dass für die jeweiligen Parameter der passende Datentyp erwartet wird. Von einer Toolbox-Funktion wie replace_nth zu erwarten, dass sie ihre Parameter auf passende Typen normiert, verletzt eigentlich die Idee der Separation of Concerns.
Abgesehen läufst Du ohne parseInt auch bei positiven Werten für occur Gefahr, dass es Fehler gibt, z.B. ergibt 3 + "1" nicht 4, sondern "31".
Ich finde übrigens deinen for-Body ziemlich umständlich; besser ist, wenn man alles, was nicht unbedingt in die Schleife hinein muss, herauszieht; und bei symmetrischem Code versucht man auch, die Symmetrie durch Umrechnen von Parametern auszunutzen. Und den Sonderfall occur=0 kann man eleganter lösen.
Meine Version von replace_nth sieht so aus (ohne Parameterprüfungen):
function replace_nth(input, search, replace, occur)
{
var items = input.split(search);
if (occur == 0) // occur=0 bedeutet replace all
return items.join(replace);
if (occur < 0) // negatives occur auf positive Sicht umrechnen
occur = items.length + occur;
occur--; // occur auf 0-basiert umrechnen
var lastItem = items.pop();
var result = '';
for(n = 0; n < items.length; n++)
{
result += items[n] + (n == occur ? replace : search);
}
return result + lastItem;
}
Eine Alternative zur Stringverkettung könnte das hier sein:
for(n = 0; n < items.length; n++)
{
items[n] = items[n] + (n == occur ? replace : search);
}
return items.join('') + lastItem;
Oder noch heftiger mit Funktionen der JS Library:
return items.map(function(item, i) {
return item + (i == occur ? replace : search);
})
.join('') + lastItem;
JavaScript ist wie APL - es erlaubt einzeilige Programme beliebiger Komplexität 😂
Allerdings muss man das erstmal auf Laufzeit messen, es könnte unterm Strich langsamer sein als einfache Stringverkettung. Die Javascript-Engines der Browser sollen hier große Unterschiede haben.
Rolf
Hello,
Abgesehen läufst Du ohne parseInt auch bei positiven Werten für occur Gefahr, dass es Fehler gibt, z.B. ergibt 3 + "1" nicht 4, sondern "31".
Kann man bei JavaScript die Datentypen für die Signatur festlegen, oder ist das genauso bescheuert variant, wie bei PHP?
Meine Version von replace_nth sieht so aus (ohne Parameterprüfungen):
function replace_nth(input, search, replace, occur) { var items = input.split(search); if (occur == 0) // occur=0 bedeutet replace all return items.join(replace); if (occur < 0) // negatives occur auf positive Sicht umrechnen occur = items.length + occur; occur--; // occur auf 0-basiert umrechnen var lastItem = items.pop(); var result = ''; for(n = 0; n < items.length; n++) { result += items[n] + (n == occur ? replace : search); } return result + lastItem; }
Hast Du ausprobiert, ob das auch mit führenden oder abschließenden "search" funktioniert? Nach meinem Verständnis müsste der Output-String unerlaubt verkürzt werden.
Ich würde mich nun freuen, wenn die Verfechter von indexOf() & Co. auch mal ihre Version vorstellen könnten ;-P
Liebe Grüße
Tom S.
Ja, habe ich getestet (weil ich mich ja gefragt habe was dein '|'+Input+'|'
sollte).
'ende'.split('e') ergibt ['', 'nd', ''], deswegen braucht man diese Einrahmung nicht.
Rolf
Hello,
Ja, habe ich getestet (weil ich mich ja gefragt habe was dein
'|'+Input+'|'
sollte). 'ende'.split('e') ergibt ['', 'nd', ''], deswegen braucht man diese Einrahmung nicht.
Nanunana! Da hatte ich mich wohl selber ausgetrickst. Hat nämlich bei mir am Anfang nicht funktioniert, erst nach dem Hinzufügen der Begrenzer. War mir als PHP-Geschädigter auch sofort klar. Bei explode() ist das nicht so.
Danke für die Lernhilfe ;-)
Liebe Grüße
Tom S.
Tach!
Kann man bei JavaScript die Datentypen für die Signatur festlegen, oder ist das genauso bescheuert variant, wie bei PHP?
Mittlerweile kann man in PHP auch Type Hints für skalare Typen angeben. In Javascript hingegen nicht. Da brauchst du andere Sprachen, wie beispielsweise TypeScript.
Es ist nicht so, dass die Verwender der Funktion DAUs und DABs (B=Bot) sind. Stattdessen wird es von Programmierern verwendet (oder solchen, die sich dafür halten). Und die haben auch eine Verantwortung, dass damit nur das geschieht, was geschehen soll. Wenn man Müll reingibt, kommt Müll raus. Ist nicht mein Problem, ist deren Müll. Dokumentieren mit JSDoc, was die Parameter für eine Bedeutung haben und welchen Typ sie erwarten, muss dafür ausreichen, statt dass du da versuchst, eine Wasserdichtheit zu erreichen, die unnötig viel Zeit benötigt und eine ebensolche Komplexität hinzufügt.
dedlfix.
Nachtrag: Habe Laufzeiten gemessen (in IE11 und Chrome)
Das hier ist die schnellste Implementierung, die mir gelungen ist, sie setzt das Ergebnis rückwärts zusammen. Vermutlich deshalb schneller, weil sie items.length nur einmal abfragt und eine Stringverkettung weniger macht. Lösungen mit join() sind schlechter, Chrome ist dabei extrem langsam, deswegen ist auch eine Lösung mit map() in Chrome nix, und Ausweichen auf reduce() (um in der Callback-Funktion wieder Stringverkettung zu betreiben) hat auch zu viel Overhead.
Selbst die Replace-All Variante ist in der for-Schleife schneller als ein join(''). Danke, Brendan!
function replace_nth(input, search, replace, occur)
{
var items = input.split(search);
if (occur < 0)
occur = items.length + occur;
occur--;
var result = items.pop();
for (var i=items.length-1; i>=0; i--)
{
result = items[i] + ((occur == -1 || i == occur) ? replace : search) + result;
}
return result;
}
Rolf
Hello,
Du siehst, dass das auch ein ideale Highlighting-Funktion sein kann für das Frontend?
Liebe Grüße
Tom S.
Hallo Rolf,
for (var i=items.length-1; i>=0; i--) { result = items[i] + ((occur == -1 || i == occur) ? replace : search) + result; }
wenn du noch etwas optimieren willst, kannst du den Fälle occur==-1
und i==occur
außerhalb der Schleife behandeln, und dann die Schleife getrennt bis und ab occur laufen lassen. Dann hast du keine Abfragen mehr in der Schleife.
Gruß
Jürgen
Ja, kann ich. Wir reden hier aber von Funktionslaufzeiten im Bereich von 400µs (Chrome) oder 1100µs (IE11), da ist das ziemlich wurscht. Es war von Anfang an wurscht, aber solche Spielereien machen einfach Spaß :)
Bei einem Android 2 Relikt mag die Wurst zum Käse werden, das habe ich aber gerade nicht zur Hand ;-)
Rolf
Hello,
falsche URL.
Zu doof, copy & paste zu benutzen tztz
Beispiel unter getscript.de/js/functions
Liebe Grüße
Tom S.
Hallo TS,
Beispiel unter getscript.de/js/functions
Was ist das denn eigentlich für eine bescheuerte komische domain? getscript.de?
Bis demnächst
Matthias
Hello,
Beispiel unter getscript.de/js/functions
Was ist das denn eigentlich für eine
bescheuertekomische domain? getscript.de?
Gefällt dir der Domainname nicht? :-)
Das ist mein öffentlicher Testeimer. Da schmeiß ich alles rein, was interaktiv zwischen verschiedenen Gerätetypen ausprobiert werden soll.
Liebe Grüße
Tom S.
Hallo TS,
Gefällt dir der Domainname nicht? :-)
Ich war verwundert, dass getscript.de eine Linkliste von Partnerbörsen ist.
Bis demnächst
Matthias
Hello,
Gefällt dir der Domainname nicht? :-)
Ich war verwundert, dass getscript.de eine Linkliste von Partnerbörsen ist.
Ich habe für meine ersten Übungen "Nachladen mit AJAX" nur die Daten von liebesfalle.de ausgeliehen, weil sie sowieso öffentlich sind und die DB vom Getscript-Host erreichbar ist. Da muss es jetzt auch mal weitergehen...
Ich kämpfe mich nur langsam durch durch meine Aufgabenlisten und bekomme dann von Zeit zu Zeit ja auch noch Mitstreiter, die an ähnlichen Sachen arbeiten und mich ablenken.
Liebe Grüße
Tom S.
@@Matthias Apsel
Was ist das denn eigentlich für eine
bescheuertekomische domain? getscript.de?
getscript zu selfscript wie gethtml zu selfhtml.
LLAP 🖖
Die replace() Funktion kann deshalb nicht angewendet werden
Wieso nicht? Die nimmt doch einen regex, mit solch man einem vielerlei bewerkstelligen könnte.
Problem: "hashimoto" beinhaltet ebenfalls das Wort "has"
Möchtest du das letzte Vorkommen von "has" entfernen, oder diesen String wenn er als Wort vorkommt? Sollte letzteres der Fall sein, böten sich sog. Word Boundaries an.
Regex101 ist ein recht praktisches Tool zum herumprobieren.
Ich habe die drei Vorschläge mal gesammelt:
output = input.split(/\bhas\b/).join(' ');
let i = input.lastIndexOf('has');
output = input.substr(0,i) + input.substr(i+4);
output = input.replace(/\bhas\b/g,' ');
Die lastIndexOf
-Variante finde ich persönlich am schlechtesten lesbar, und der Teufel steckt hier noch mehr im Detail: Die beiden anderen Methoden entfernen automatisch alle freistehenden Vorkommen von 'has'. Bei lastIndexOf
wird nur das letzte Vorkommen entfernt, was vermutlich so nicht gewollt ist. Man müsste die Methode also in einer Schleife oder Rekursion anwenden, um das gewollte Ergebnis zu erhalten. Außerdem muss man noch Fallunterscheidungen für einige Spezialfälle treffen: 'has' am Ende oder Anfang eines Satzes und nach oder vor einem Satzzeichen.
Da finde ich input.replace(/\bhas\b/g, ' ')
am elegantesten (und im Übrigen auch am schnellsten).
Servus!
Bei
lastIndexOf
wird nur das letzte Vorkommen entfernt, was vermutlich so nicht gewollt ist.
Frage des Thread-Erstellers war:
wie kann man das letzte Vorkommen von "has" entfernen?
Duck und wech!
Matthias Scharwies
Frage des Thread-Erstellers war:
wie kann man das letzte Vorkommen von "has" entfernen?
Aber der folgende Zusatz…
Problem: "hashimoto" beinhaltet ebenfalls das Wort "has".
… deutet darauf hin, dass das eigentliche Problem ist, freistehende Vorkommen von 'has' zu entfernen. Im Beispiel-Satz ist das schicksalhafter Weise eben auch das letzte Vorkommen.
Hallo,
"user hashimoto has two dogs"
wie kann man das letzte Vorkommen von "has" entfernen? (Problem: "hashimoto" beinhaltet ebenfalls das Wort "has". Die replace() Funktion kann deshalb nicht angewendet werden)
Wie du am Threadverlauf sehen kannst, gibt es unterschiedliche Auffassungen, wie deine Aufgabe zu interpretieren ist. Demzufolge gibt es mehrere sinnvolle Lösungen.
Du müsstest:
Gruß
Kalk