Replace placeholders (v4)

Revision 4 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 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 replaceByMap4 = (str, replaceMap) => {
    const queries = Object.keys(replaceMap);
    const results = [];
    let i = 0;
    const li = str.length;
    let j = 0;
    const lj = queries.length;
    let q = 0;
    let qs = '';
    let lq = 0;
    let count = 0;
    for (i = 0; i < li && count !== lj; ++i) {
       for(j = 0; j < lj; ++j) {
           qs = queries[j];
           lq = qs.length;
           for(q = 0; q < lq && str[i+q] === qs[q]; ++q) {
               if (q === lq - 1) {
                   results.push({id: qs, start: i, content: replaceMap[qs]});
                   queries[j] = '';
                   count++;
               }
           }
        }
    }
    
    return results.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, results[0].start));
};
const replaceByMap5 = (str, replaceMap) => {
    const queries = Object.keys(replaceMap);
    let result = '';
    let i = 0;
    const li = str.length;
    let j = 0;
    const lj = queries.length;
    let q = 0;
    let qs = '';
    let lq = 0;
    let count = 0;
    let tailIndex = 0;
    let found = false;
    for (i = 0; i < li && count !== lj; ++i) {
       for(j = 0; j < lj; ++j) {
           qs = queries[j];
           lq = qs.length;
           for(q = 0; q < lq && str[i+q] === qs[q]; ++q) {
               if (q === lq - 1) {
                   result += str.substring(tailIndex, i) + replaceMap[qs];
                   tailIndex = i + lq;
                   queries[j] = '';
                   count++;
                   found = true;
               }
           }
           if (found) {
               found = false;
               break;
           }
        }
    }
    if (!tailIndex) {
    	return result;
    }
    return result + str.substring(tailIndex);
};
const replaceByMap6 = (str, replaceMap) => {
    const queries = Object.keys(replaceMap);
    let result = '';
    let count = 0;
    let tailIndex = 0;
    let found = false;
    for (let i = 0; i < str.length && count !== queries.length; ++i) {
       for(let j = 0; j < queries.length; ++j) {
           const qs = queries[j];
           const lq = qs.length;
           for(let q = 0; q < lq && str[i+q] === qs[q]; ++q) {
               if (q === lq - 1) {
                   const head = str.substring(tailIndex, i);
                   tailIndex = i + lq;
                   result += head + replaceMap[qs];
                   queries[j] = '';
                   count++;
                   found = true;
               }
           }
           if (found) {
               found = false;
               break;
           }
        }
    }
    return result + str.substring(tailIndex);
};
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))],
]);

Test runner

Ready to run.

Testing in
TestOps/sec
multiply replace
replaceByMap0(bigStr, replaceMap)
ready
procedure indexOf + reduce
replaceByMap4(bigStr, replaceMap)
ready
full procedure
replaceByMap5(bigStr, replaceMap)
ready
full procedure 2
replaceByMap6(bigStr, replaceMap)
ready
multiply indexOf + reduce
replaceByMap2(bigStr, replaceMap)
ready

Revisions

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