Replace placeholders (v3)

Revision 3 of this benchmark created on


Setup

const replaceByMap0 = (str, replaceMap) => {
    return Object.keys(replaceMap).reduce((acc, id) => {
        return acc.replace(id, replaceMap[id]);
    }, str);
};
const chartsForEscape = /([^a-zA-Z0-9])/g;
const escapeRegExpSyntax = (str) => str.replace(chartsForEscape, '\\$1');
const replaceByMap1 = (str, replaceMap) => {
    const regexp = new RegExp(Object.keys(replaceMap)
        .map((s) => `(${escapeRegExpSyntax(s)})`)
        .join('|'), 'g');
    return str.replace(regexp, (str) => replaceMap[str] ?? str);
};
const replaceByMap1Cached = (str, replaceMap) => {
    return str.replace(regexp, (str) => replaceMap[str] ?? str);
};
const replaceByMap2 = (str, replaceMap) => {
    const indexes = Object.keys(replaceMap)
        .map((id) => {
        const start = str.indexOf(id);
        if (start === -1) {
            return undefined;
        }
        return {
            start,
            id,
            content: replaceMap[id],
        };
    })
        .filter((x) => Boolean(x))
        .sort((a, b) => a.start - b.start);
    if (!indexes.length) {
        return str;
    }
    return indexes.reduce((acc, { start, id, content }, i, arr) => {
        const next = arr[i + 1];
        const tailPart = str.substring(start + id.length, next ? next.start : undefined);
        return acc + content + tailPart;
    }, str.substring(0, indexes[0].start));
};
const replaceByMap3Cached = (str, replaceMap) => {
    return str
        .split(regexp)
        .map((s) => replaceMap[s] ?? s)
        .join('');
};
const replaceByMap3 = (str, replaceMap) => {
    const regexp = new RegExp(Object.keys(replaceMap)
        .map((s) => `(${escapeRegExpSyntax(s)})`)
        .join('|'), 'g');
    return str
        .split(regexp)
        .map((s) => replaceMap[s] ?? s)
        .join('');
};
const replaceByMap4 = (str, replaceMap) => {
    const queries = Object.keys(replaceMap);
    const results = {};
    let i = 0;
    const mi = str.length;
    let j = 0;
    const mj = queries.length;
    let q = 0;
    let qs = '';
    let qm = 0;
    let count = 0;
    for (i = 0; i < mi && count !== qm; ++i) {
       for(j = 0; j < mj; ++j) {
           qs = queries[j];
           qm = qs.length;
           for(q = 0; q < qm && str[i+q] === qs[q]; ++q) {
               end = false;
               if (q + 1 === qm) {
                   results[qs] = i;
                   queries[j] = '';
                   count++;
               }
           }
        }
    }
    const indexes = Object.keys(replaceMap).map((id) => ({id, content: replaceMap[id], start: results[id]})).sort((a, b) => a.start - b.start);
    
    return indexes.reduce((acc, { start, id, content }, i, arr) => {
        const next = arr[i + 1];
        const tailPart = str.substring(start + id.length, next ? next.start : undefined);
        return acc + content + tailPart;
    }, str.substring(0, indexes[0].start));
};
function makeBigStr(length) {
    let result = '';
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ()/?><';
    const charactersLength = characters.length;
    let counter = 0;
    while (counter < length) {
        result += characters.charAt(Math.floor(Math.random() * charactersLength));
        counter += 1;
    }
    return result;
}
const bigStr = makeBigStr(2 * (1 << 20));
const getRandomSubstr = (length, str) => {
    const start = Math.floor(Math.random() * str.length - length);
    return str.substring(start, start + length);
};
const replaceMap = Object.fromEntries([
    [getRandomSubstr(20, bigStr), makeBigStr(5 * (1 << 10))],
    [getRandomSubstr(20, bigStr), makeBigStr(5 * (1 << 10))],
    [getRandomSubstr(10, bigStr), makeBigStr(4 * (1 << 20))],
    [getRandomSubstr(20, bigStr), makeBigStr(5 * (1 << 10))],
]);
const regexp = new RegExp(Object.keys(replaceMap)
        .map((s) => `(${escapeRegExpSyntax(s)})`)
        .join('|'), 'g');

Test runner

Ready to run.

Testing in
TestOps/sec
multiply replace
replaceByMap0(bigStr, replaceMap)
ready
replace(regexp, () => {})
replaceByMap1(bigStr, replaceMap)
ready
multiply indexOf + reduce + substring
replaceByMap2(bigStr, replaceMap)
ready
split + join
replaceByMap3(bigStr, replaceMap)
ready
replace(regexp, () => {}) with RegExp cache
replaceByMap1Cached(bigStr, replaceMap)
ready
parallel indexOf + reduce
replaceByMap4(bigStr, replaceMap)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.