Javascript:字符串

时间:2016-05-05 18:34:27

标签: javascript string dictionary caching replace

我正在寻找一种有效的方法(如:尽可能少的资源)用Javascript中的其他字符串替换字符串的orrurances。 重点是计算时间,而不是内存消耗。

搜索词和替换词作为用作词典的对象

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

目前我正在迭代密钥并构建一个正则表达式(带有set g标志),然后查找字典中的每个匹配并替换它:

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()];
    });
}

这很有效,但我每次都需要编译一个新的常规表达式。我的问题是:有人可以提出一种有效的方法来缓存常规表达式,如果是相同的映射(与“相同的键”相同,既不是“相同的对象”也不是“相同的值”,也不是“相同的键顺序”)再次给出正则expresiosn重新使用?

一种显而易见的方法是排序,序列化和散列密钥,将其用作存储常规表达式的密钥,并在将来调用时存在时重新使用存储的正则表达式。但是,我认为这很可能比每次编译一个新的常规表达需要更多的时间......

思想/输入?

编辑:RegExp.escape()是一个函数,用于转义字符串中的特殊字符以用于正则表达式:

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

使用信息:

  • 替换完成很多,就像我在聊天系统中使用的那样
  • 对替换地图的更改很少,但这取决于聊天操作员如何使用该功能。自动脚本可以自动且频繁地添加和删除替换规则。但是,替换地图的更改总是比将替换地图应用于字符串的频率低。
  • 一个或多个替换地图可能同时使用并且彼此独立。

1 个答案:

答案 0 :(得分:0)

这就是我提出的:

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);
    }
}

用法:

// ---- 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