2016-05-05 3 views
0

Ich bin auf der Suche nach einem Weg zu effektiv (wie in: mit so wenig Ressourcen wie möglich) ersetzen orrurances von Zeichenfolgen durch andere Zeichenfolgen in Javascript. Der Fokus liegt auf Rechenzeit, mehr als Speicherverbrauch.Javascript: effektive Array-basierte Ersetzung in Strings

Suchbegriffe und Ersetzungen werden als Objekt verwendet als Wörterbuch gegeben

var replacements = { 
    search : 'replace', 
    another : 'replacement', 
    'and one' : 'more' 
} 

Zeit die Tasten ich bin Iterieren über und den Aufbau einer regexp (mit Set g-Flag) aus ihnen heraus, dann jedes Spiel sehen im Wörterbuch und ersetzt es:

String.prototype.mapReplace = function (map, replaceFullOnly = false) { 
    var regexp = []; 
    for (var key in map) { 
     regexp.push(RegExp.escape(key)); 
    } 
    regexp = regexp.join('|'); 
    if (replaceFullOnly) { 
     regexp = '\\b(?:' + regexp + ')\\b'; 
    } 
    regexp = new RegExp(regexp, 'gi'); 
    return this.replace(regexp, function (match) { 
     return map[match.toLowerCase()]; 
    }); 
} 

Dies funktioniert aber ich brauche eine neue regelmäßige expresison jedes Mal zu kompilieren. Meine Frage ist: Kann jemand eine effektive Möglichkeit finden, die regulären Expresionen zwischenzuspeichern und, wenn die gleiche Karte (wie in "gleichen Schlüsseln", weder "dasselbe Objekt" noch "gleiche Werte" noch "gleiche Reihenfolge der Schlüssel") ist wieder gegeben, die regulären expresiosn wird wiederverwendet?

Eine naheliegende Methode wäre das Sortieren, Serialisieren und Hashing von Schlüsseln. Verwenden Sie sie als Schlüssel, um die reguläre Ausdrucke zu speichern und gespeicherte reguläre Ausdrücke wiederzuverwenden, wenn sie in zukünftigen Aufrufen vorhanden sind. Allerdings werde ich wahrscheinlich mehr Zeit als das Erstellen einer neuen regulären expresiosn jedes Mal benötigen ...

Ideen/Eingabe?


Edit: Regexp.escape() ist eine Funktion, die in regulären Ausdrücken Sonderzeichen in Zeichenketten für den Einsatz entkommt:

RegExp.escape= function(s) { 
    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 
}; 

Nutzungsinformationen:

  • Ersatz sind viel getan, wie dies ich in einem Chat-System verwendet
  • Änderungen an der Ersatzkarten sind selten, jedoch hängt es davon ab, wie Chat-Betreiber die Funktion nutzen. Automatisierte Skripte zum automatischen und häufigen Hinzufügen und Entfernen von Ersetzungsregeln sind möglich. Änderungen an der Ersetzungszuordnung werden jedoch immer seltener vorgenommen als die Ersatzzuordnung für Zeichenfolgen.
  • eine oder mehrere Ersatzkarten können gleichzeitig und unabhängig voneinander verwendet werden.
+0

tun Sie einige Beispiele haben, was die mapreplace tut? –

+0

Warum werden die kompilierten Regexes nicht explizit zwischengespeichert? Übergeben Sie nicht jedes Mal verschiedene Kartenobjekte. – Bergi

+0

@NinaScholz Es wird in einem Chat-System verwendet, in dem der Benutzer neue Ersetzungsregeln dynamisch festlegen kann. Es existieren verschiedene Regelsätze für verschiedene Benutzer. –

Antwort

0

Das ist, was ich kam mit:

var ReplacementMap = function (map, replaceFullOnly, ignoreCase) { 

    var regexp = null; 

    var update = function (search, replacement) { 
     if (!isDefined(replacement)) { 
      if (!(search in map)) return; 
      delete map[search]; 
     } else { 
      if (map[search] == replacement) return; 
      map[search] = replacement; 
     } 
     invalidateRegexp(); 
    } 

    var buildRegexp = function() { 
     if (regexp != null) return; 
     regexp = []; 
     for (var key in map) { 
      regexp.push(RegExp.escape(key)); 
     } 
     regexp = regexp.join('|'); 
     if (replaceFullOnly) { 
      regexp = '\\b(?:' + regexp + ')\\b'; 
     } 
     regexp = new RegExp(regexp,'g' + (ignoreCase ? 'i' : '')); 
    } 

    var invalidateRegexp = function() { 
     regexp = null; 
    } 

    Object.defineProperties(this, { 
     fullOnly : { 
      set : value => { 
       if (replaceFullOnly == value) return; 
       replaceFullOnly = !!value; 
       invalidateRegexp(); 
      }, 
      get :() => replaceFullOnly 
     }, 
     ignoreCase : { 
      set : value => { 
       if (ignoreCase == value) return; 
       ignoreCase = !!value; 
       invalidateRegexp(); 
      }, 
      get :() => ignoreCase 
     } 
    }); 

    this.set = function set (search, replacement) { 
     if (Array.isArray(search)) { 
      if (Array.isArray(search[0])) { 
       search.forEach(function (search) { 
        set(search); 
       }); 
      } else { 
       update(search[0], search.length > 1 ? search[1] : undefined); 
      } 
     } else if (search instanceof Object 
       && search !== null 
       && !String.isString(search)) { 
      for (key in search) { 
       update(key, search[key]); 
      } 
     } else update(search, replacement); 
    } 

    this.get = function (search) { 
     return search in map ? map[search] : undefined; 
    } 

    this.remove = function(search) { 
     update(search); 
    } 

    this.apply = function (string) { 
     buildRegexp(); 
     return string.replace(regexp, function (match) { 
      return map[match.toLowerCase()]; 
     }); 
    } 

    this[Symbol.iterator] = function*() { 
     for (let key of Object.keys(map)) { 
      yield {[key] : map[key]}; 
     } 
     return; 
    } 

    if (isDefined(replaceFullOnly)) { 
     replaceFullOnly = !!replaceFullOnly; 
    } else { 
     replaceFullOnly = true; 
    } 

    if (isDefined(ignoreCase)) { 
     ignoreCase = !!ignoreCase; 
    } else { 
     ignoreCase = true; 
    } 

    if (isDefined(map)) { 
     let entries = map; 
     map = Object.create(null); 
     this.set(entries); 
    } else { 
     map = Object.create(null); 
    } 
} 

Verbrauch:

// ---- CREATE MAP ---- 

// Empty Map 
var m0 = new ReplacementMap(); 

// Map initialized with one replacement: foo => bar 
var m1_1 = new ReplacementMap('foo','bar'); 
var m1_2 = new ReplacementMap(['foo','bar']); 
var m1_3 = new ReplacementMap({foo : 'bar'}); 

// Map initialized with two replacements: foo => bar, fooz => baz 
var m2_1 = new ReplacementMap([['foo','bar'], ['fooz', 'baz']]); 
var m2_2 = new ReplacementMap({foo : 'bar', fooz : 'baz'}); 
var m2_3 = new ReplacementMap([{foo : 'bar'}, {fooz : 'baz'}]); 

// ---- ADD/MODIFY ENTRIES ---- 
var m0.set(...) // ... parameters work the same as in the constructor 

// ---- REMOVE ENTRIES ---- 
var m2_1.delete('foo') // removes replacement rule for foo => bar 
var m2_1.delete('test') // fails silently 

// ---- READ ENTRIES ---- 
var m2_1.get('foo') // returns "bar" 
var m2_1.get('test') // returns undefined; 

for (rule of m2_1) { 
    alert(JSON.stringify(rule)); 
} 
// alerts "{'foo':'bar'}" and "{'fooz':'baz'}" 


// ---- APPLY ON STRING ---- 
alert(m2_1.apply("foo bar")) // bar bar 

// change behaviour: 
m2_1.fullOnly = true; // replace foo with bar, but not foobar with barbar 
         // default: true; 

m2_1.ignoreCase = true; // ignore case. default: true