Javascript - Entfernen von wiederholenden Phrasen in String?
Kurz. Ich habe einen String:
var str = `Emojibombe Pogchamp Pogchamp Pogchamp Pogcham LiEmoji LiEmoji LiEmoji`;
Ich kenn mich mit Javascript ganz gut aus. Aber irgendwie häng ich aufm Schlauch, wie ich aus diesem string dann folgenden strink bekommen würde:
var result = `Emojibombe Pogchamp LiEmoji`;
Quasi eine Funktion, die Teile, die sich direkt in Folge wiederholen, kürzt. Hierbei wäre halt die 4 Mal Pogchamp und die 3 mal LiEmoji auf jeweils 1 gekürzt.
Es soll aber eben nicht greifen, wenn sowas drin steht:
var str = `Emojibombe Pogchamp LiEmoji Pogchamp LiEmoji Pogchamp LiEmoji`;
Wer kann mir da aushelfen? :/ Auch andere Programmiersprachen können mir helfen.
Wär für ne Lösung meines Problems dankbar.
3 Antworten
Was ist, wenn es keine Leerzeichen oder andere Trennzeichen gibt. Performance ist zweitrangig
Ich würde das mit filter() lösen:
const result = str.split(" ")
.filter((word, i, arr) => arr[i-1] !== word)
.join(" ");
Neue Lösung, da Wörter nicht unbedingt durch Leerzeichen getrennt sein müssen:
let str = `Emojibombe PogchampPogchampPogchampPogchamp LiEmoji LiEmoji LiEmoji Eine Nachricht dazwischen Pogchamp Pogchamp Pogchamp Pogcham LiEmoji LiEmoji LiEmoji`;
str = str.split(/\s+/g).filter((word, i, words) => words[i-1] !== word).join(" ");
for (let wl = Math.min(20, Math.floor(str.length/2)); wl > 1; wl--) { // word length
let wordFoundAt = [];
for (let i = wl; i <= str.length - wl; i++) {
const word = str.substring(i,i+wl);
if (word.includes(" ")) continue;
const lastWord = str.substring(i-wl, i);
if (lastWord === word) {
for(let j = i; j < i+wl; j++) {
wordFoundAt.push(j);
}
}
}
str = str.split("").filter((_,i) => !wordFoundAt.includes(i)).join("");
}
console.log(str);
Und die "Wörter" welche sich wiederholen beginnen wirklich immer mit nem Großbuchstaben?
Oder hast du das nur groß geschrieben damit man's besser erkennt und es könnte auch "Emojibombe pogchamppogchamppogchamppogchamp LiEmoji" sein?
Falls letzteres, Stammen die Wörter wenigstens aus einem Wort-Pool (also sind das bestimmte Wörter, von denen du am besten auch eine Liste hast) oder soll das für alle Wörter gelten? Aber ein normales Wort wie "Benennen" würde dann zu "Benen" geküzt werden...
Erstmal alle doppelten Wörter mit Leerzeichen dazwischen entfernen, die Leerzeichen machen sonst später Probleme. Dafür habe ich ja bereits eine Lösung genannt.
Und dann alle Wörter entfernen die direkt nacheinander stehen sind. Ich bin davon ausgegangen dass so ein Wort maximal 20 Zeichen lang ist, kann man aber beliebig höher machen die word length:
str = str.split(/\s+/g).filter((word, i, words) => words[i-1] !== word).join(" ");
for (let wl = 20; wl > 1; wl--) { // word length
let wordFoundAt = [];
for (let i = wl; i <= str.length - wl; i++) {
const word = str.substring(i,i+wl);
if (word.includes(" ")) continue;
const lastWord = str.substring(i-wl, i);
if (str.substring(i-wl, i) === str.substring(i,i+wl)) {
console.log(str.substring(i-wl, i), str.substring(i,i+wl));
for(let j = i; j < i+wl; j++) {
wordFoundAt.push(j);
}
}
}
str = str.split("").filter((_,i) => !wordFoundAt.includes(i)).join("");
}
console.log(str);
Ups der console.log in der Mitte kann natürlich raus, war nur fürs Debuggen.
Hier nochmal verbessert:
let str = `Emojibombe PogchampPogchampPogchampPogchamp LiEmoji LiEmoji LiEmoji Eine Nachricht dazwischen Pogchamp Pogchamp Pogchamp Pogcham LiEmoji LiEmoji LiEmoji`;
str = str.split(/\s+/g).filter((word, i, words) => words[i-1] !== word).join(" ");
for (let wl = Math.min(20, str.length/2); wl > 1; wl--) { // word length
let wordFoundAt = [];
for (let i = wl; i <= str.length - wl; i++) {
const word = str.substring(i,i+wl);
if (word.includes(" ")) continue;
const lastWord = str.substring(i-wl, i);
if (lastWord === word) {
for(let j = i; j < i+wl; j++) {
wordFoundAt.push(j);
}
}
}
str = str.split("").filter((_,i) => !wordFoundAt.includes(i)).join("");
}
console.log(str);
Die wörter entsprechen nicht den regulären wörtern. Ich möchte wiederholende Phrasen innerhalb eines Textes ermitteln. Dabei wär eine Abtrennung von Wörtern mit MINDESTENS 4 zeichen bis maximal 15 ideal. Dazu wäre es nicht schlimm, wenn der entstehende Text "zusammenklebende" wörter erzeugt. Ich habe hier mal einen Test geschrieben:
function parseInput(t)
{
var r = t;
var beginPos = 0;
var minLength = 4;
var maxLength = 15;
while(beginPos < r.length - minLength)
{
var searchFor = [];
var currentMaxLength = Math.min(maxLength, r.length - beginPos - minLength);
for(var offsetLength = minLength; offsetLength <= currentMaxLength;offsetLength++)
{
var preText = r.substr(0, beginPos + offsetLength);
var phrs = r.substr(beginPos, offsetLength);
var parts = [];
for(var tmpOffset = phrs.length + beginPos;tmpOffset < r.length;tmpOffset += phrs.length)
{
var off = r.substr(tmpOffset, phrs.length);
if(off == "") break;
parts.push(off);
}
if(parts.indexOf(phrs, 1) == 1)
{
var outp = "";
for(var n = 1;n < parts.length;n++)
{
if(parts[n] != phrs)
{
outp += parts[n];
}
}
r = preText + outp;
}
}
beginPos += 1;
}
if(t == r)
{
return(r);
}
else
{
return(parseInput(r));
}
}
Testergebnisse:
var currentPhrase = `Ich glaube, damit habe ich das Problem ja gelöst. Rekursiv ist die ganze Sache sache sache sache sache auch noch PogoChampPogoChampPogoChampPogoChamp Und Und Und und und und UndUndUnd Es kommt auch mit komplexen Dingern klar`;
var result = parseInput(currentPhrase);
// Ich glaube, damit habe ich das Problem ja gelöst. Rekursiv ist die ganze Sache sache auch noch PogoChamp Und undUndUnd Es kommt auch mit komplexen Dingern klar
Hierbei sucht er alles, was mindestens 3 mal in Folge auftaucht und enfernt es dann daraus.
Und ja. Das ganze kann mit 100%iger Wahrscheinlichkeit noch stark optimiert werden.
Spitzenklasse. O_O Ist definitiv effektiver und besser als mein Ansatz. Hab auch meinen Text mal durchgejagt und er macht exakt das, was er soll. VIELEN dank. :)
Gerne, war ein schönes Rätsel wie man das am besten löst! ^^
Die for-Schleife kannst übrigens du zu
for (let wl = Math.min(15, str.length/2); wl > 3; wl--) { // word length
ändern, dann geht es alle Wortlängen von 4-15 durch.
Um nur Wörter zu entfernen die dreimal in Folge sind muss man es noch etwas abändern, aber das schaffst du :D
Aber das hast du wahrscheinlich eh bereits rausgefunden. :D
Den String kannst du zunächst anhand des Leerzeichens aufsplitten. Dann iterierst du über das resultierende Array und kopierst dabei jeden Eintrag hinüber in ein anderes Array. Wenn dabei das aktuelle Element dem letzten Element des neuen Arrays entspricht (sofern dieses überhaupt schon einen Eintrag hat), überspringst du dieses.
Idee gut. Nutzen leider ... weniger :/
Grund: Es können auch solche Abfolgen auftauchen:
Wie würde ich daraus am effektivsten die Dinge erkennen, welche sich mindestens 2 mal in folge wiederholen. Also (Beispiel) ProPro oder KartKartKartKart oder Tom Tom Tom ... usw.
Vorzugsweise mit einer Referenz auf das Wort, welches sich da wiederholt, damit man es halt kürzen bzw. ersezten könnte.