Ich habe eine Zeichenfolge, die so aussieht

someString = "#3Hello there! How many #4candies did you sell today? Do have any #4candies left?"

lookupDict = {"Hello there": "#3", "candies": "#4"}

Jetzt möchte ich alle Begriffe in der Zeichenfolge someString durch #0 ersetzen, die nicht im Wörterbuch lookupDict enthalten sind. Ich kann nicht durch ein Leerzeichen " " teilen, da dadurch bestimmte Begriffe wie Hello there als zwei verschiedene Wörter Hello und there angezeigt werden und dies niemals meinem Zustand entsprechen würde.

Jetzt weiß ich, dass ich einen einfachen regulären Ausdruck anwenden muss, der vor jedem Wort ein #0 einfügt. Zum Beispiel so etwas wie

let regex = /(\b\w+\b)/g;

someString = someString.replace(regex, '#0$1'));

Dies würde jedoch blind #0 zu jedem Begriff hinzufügen und nicht im Wörterbuch lookupDict nachschlagen.

Gibt es eine Möglichkeit, den regulären Ausdruck mit einer Suche im Wörterbuch zu kombinieren und das #0 entsprechend zuzuweisen? Grundsätzlich würde das Endergebnis so etwas wie

someString = "#3Hello there! #0How #0many #4candies #0did #0you #0sell #0today? #0Do #0have #0any #4candies #0left?"

Hinweis: Leerzeichen können hier als Wortgrenzen betrachtet werden.

1
Souvik Ray 23 Feb. 2020 im 21:30

3 Antworten

Beste Antwort

Sie können die folgende Logik verwenden:

  • Erstellen Sie ein Array von Teilzeichenfolgen, die Sie überspringen müssen und die verkettete value s und key s des assoziativen Arrays sind
  • Sortieren Sie die Elemente nach Länge in absteigender Reihenfolge, da die Wortgrenzen bei Phrasen mit Leerzeichen möglicherweise nicht gut funktionieren
  • Kompilieren Sie ein Regex-Muster, das aus zwei Alternativen besteht: Das erste entspricht den Array-Elementen (die zur Verwendung in einem Regex-Muster maskiert sind), die einer Erfassungsgruppe beigefügt sind, und das andere entspricht den übrigen "Wörtern".
  • Wenn eine Übereinstimmung gefunden wird, prüfen Sie, ob Gruppe 1 übereinstimmt. Wenn Gruppe 1 übereinstimmt, geben Sie einfach den Übereinstimmungswert zurück, andernfalls fügen Sie #0 zum Übereinstimmungswert hinzu.

Hier ist die Implementierung:

let someString = "#3Hello there! How many #4candies did you sell today? Do have any #4candies left? #0how #0much";
const lookupDict = {"Hello there": "#3", "candies": "#4", "how": "#0", "much": "#0"};
let patternDict = [];                             // Substrings to skip
for (var key in lookupDict) {                     
  patternDict.push( `${lookupDict[key]}${key}` ); // Values + keys
}
patternDict.sort(function(a, b){                  // Sorting by length, descending
  return b.length - a.length;
});
var rx = new RegExp("(?:^|\\W)(" + patternDict.map(function(m) { // Building the final pattern
    return m.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');}
  ).join("|") + ")(?!\\w)|\\S+", "gi");
// rx = /(?:^|\W)(#3Hello there|#4candies|#0much|#0how)(?!\w)|\S+/gi
someString = someString.replace(rx, (x, y) => y ? x : `#0${x}` );
console.log(someString);
// => #3Hello there! #0How #0many #4candies #0did #0you #0sell #0today? #0Do #0have #0any #4candies #0left? #0how #0much

Die Regex wird so aussehen

/(?:^|\W)(#3Hello there|#4candies|#0much|#0how)(?!\w)|\S+/gi

Weitere Informationen finden Sie in der Regex-Demo (PHP-Option ausgewählt, um Gruppen grün hervorzuheben).

Details:

  • (?:^|\W) - Eine nicht erfassende Gruppe, die entweder dem Anfang der Zeichenfolge (^) oder (|) einem beliebigen Nicht-Wort-Zeichen (= ein anderes Zeichen als ein ASCII-Buchstabe, eine Ziffer oder {{) entspricht X3}})
  • (#3Hello there|#4candies|#0much|#0how) - Erfassen der Gruppe 1, die mit einem der lookupDict verketteten Werte + Schlüssel übereinstimmt
  • (?!\w) - Ein negativer Lookahead, der die Übereinstimmung nicht besteht, wenn unmittelbar rechts vom aktuellen Speicherort ein Wort char steht
  • | - oder
  • \S+ - 1+ Zeichen ohne Leerzeichen.
1
Wiktor Stribiżew 24 Feb. 2020 im 14:18

Sie können eine Funktion als zweiten Parameter an .replace übergeben und das übereinstimmende Token im Wörterbuch überprüfen

Ich habe den regulären Ausdruck so geändert, dass # nicht in die Ergebnisse aufgenommen wird

Hello there ist problematisch, wie lange kann ein einzelner Begriff sein? max 2 Wörter?

someString = "#3Hello there! How many #4candies did you sell today? Do have any #4candies left?"

let regex = /(?<!#)(\b\w+\b)/g;

someString = someString.replace(regex, x => {
// check x in dict
	return `#0${x}`
});
console.log(someString)
-1
Jacek Rojek 23 Feb. 2020 im 18:52

Auf diese Weise müssen Sie sich keine Gedanken über die Länge des LookupDict-Schlüssels oder etwas anderes machen:

let someString =
  "#3Hello there! How many #4candies did you sell today? #3Hello there! Do have any #4candies left?#3Hello there! #7John Doe! some other text with having #7John Doe person again";

const lookupDict = { "Hello there": "#3", candies: "#4", "John Doe": "#7" };

Object.keys(lookupDict).map((key, i) => {
  const regex = new RegExp(key, "g");
  someString = someString.replace(regex, lookupDict[key]); // replace each key to the value: Hello world => #3
});

someString = someString.replace(/ /gi, " #0"); // replace each space

Object.keys(lookupDict).map((key, i) => {
  const regex = new RegExp(lookupDict[key] + lookupDict[key], "g");
  someString = someString.replace(regex, `${lookupDict[key]}${key}`); // role back the value to key+value
});

someString = someString.replace(/#0#/gi, "#"); // replace #0 for each lookupDict key value

console.log(someString, '<TheResult/>');
1
Hamed Navabian 23 Feb. 2020 im 20:00