我正在尝试(使用 Javascript)将 许多 子字符串的第一次 替换为可能包含原始字符串的混合的其他子字符串子串。我认为展示一个例子可能是最简单的。
假设我有一张地图
const MAP = {
"dog": "big dog",
"big": "huge",
"cat": "mouse"
}
和一个段落
<p id="paragraph">I have a dog, a big cat, another dog, and another big cat.</p>
加载窗口后,我想将其更改为
<p id="paragraph">I have a big dog, a huge mouse, another dog, and another big cat.</p>
几乎可以做到这一点的一种方法是:
var p = document.getElementById('paragraph');
window.onload = function() {
var re = new RegExp(Object.keys(MAP).join("|"), "gi");
p.innerHTML = p.innerHTML.replace(re, function(match) {return MAP[match];});
}
但这会返回 I have a big dog, a huge mouse, another big dog, and another huge mouse.
这并不奇怪,因为 g
标签确保所有出现的都被替换,而我只想要第一次出现。
那么我的第二次尝试如下:
var p = document.getElementById('paragraph');
var l = Object.keys(MAP);
window.onload = function() {
for(var j=0; j<l.length; j++) {
p.innerHTML = p.innerHTML.replace(l[j], MAP[l[j]]);
}
}
但这会返回 I have a huge dog, a big mouse, and another dog, and another big cat.
那是因为 dog
已被 big dog
替换,然后 big
被替换为 huge
而不是 {{1 }} 在 big
之前。
如何以非迭代方式替换字符串中第一次出现的多个子字符串?这似乎是本网站之前可能已经问过的那种事情,但我一直无法在任何地方找到它。任何帮助将不胜感激,谢谢!
答案 0 :(得分:1)
由于您只想替换第一个匹配项,我认为最好的方法是用一些占位符文本准备字符串,然后进行替换。我不喜欢将同一个循环运行两次的想法,但没有其他方法可以准备字符串。
const MAP = {
"dog": "big dog",
"big": "huge",
"cat": "mouse"
}
const p = document.querySelector("p");
let t = p.innerText;
Object.keys(MAP).forEach(x => {
let parts = t.split(x);
t = `${parts.shift()}%%${x.toUpperCase()}%%${parts.join(`${x}`)}`;
});
Object.keys(MAP).forEach(x => {
t = t.replace(`%%${x.toUpperCase()}%%`, MAP[x]);
})
console.log(t)
p.innerText = t;
//<p id="paragraph">I have a big dog, a huge mouse, another dog, and another big cat.</p>
<p id="paragraph">I have a dog, a big cat, another dog, and another big cat.</p>
答案 1 :(得分:1)
让我们将字符串分成两部分:已处理(左)和尚未处理(右)。在每一步中,在右侧找到一个搜索字符串,并选择第一个。在关键字之前添加切片并将替换添加到左侧部分。搜索后切下右侧的部分。重复直到找不到搜索字符串
function replace(str, map) {
let searches = new Set(Object.keys(map))
let left = '', right = str
while (1) {
let pos = Infinity, search = ''
for (let s of searches) {
let i = right.indexOf(s)
if (i >= 0 && i < pos) {
pos = i;
search = s;
}
}
if (!search)
break
left += right.slice(0, pos) + map[search]
right = right.slice(pos + search.length)
searches.delete(search)
}
return left + right
}
//
const MAP = {
"dog": "big dog",
"big": "huge",
"cat": "mouse"
}
text = "I have a dog, a big cat, another dog, and another big cat."
console.log(replace(text, MAP))
这里有一个更简单的正则表达式解决方案:
function replace(str, map) {
let used = new Set,
re = new RegExp(Object.keys(map).join('|'), 'g')
return str.replace(re, m => {
if (used.has(m))
return m
used.add(m)
return map[m]
})
}
//
const MAP = {
"dog": "big dog",
"big": "huge",
"cat": "mouse"
}
text = "I have a dog, a big cat, another dog, and another big cat."
console.log(replace(text, MAP))
或者,如果您喜欢“压缩”代码,
let replace = (str, map, used = {}) => str.replace(
new RegExp(Object.keys(map).join('|'), 'g'),
m => used[m] ? m : map[used[m] = m])
答案 2 :(得分:1)
您可以使用 for(... in ...)
循环来实现这一点,因为默认情况下替换仅适用于第一场比赛。为了不替换两次(fe big
from big dog
),您可以使用两个辅助变量:一个用于构建新字符串(newP
)和一个用于替换单词(tempP
).
替换单词后,您必须通过删除包括单词的第一部分来更新原始字符串 (paragraph
)。例如,这可以通过 slice()
来完成。之后,您只需要将删除的部分添加到新字符串中。
在 for 循环之后,您可以简单地使用 innerHTML
将新字符串和原始字符串的其余部分插入到段落中。
工作示例:
const MAP = {
"dog": "big dog",
"big": "huge",
"cat": "mouse"
}
let newP = '';
window.addEventListener('DOMContentLoaded', function() {
let paragraph = document.getElementById('paragraph').textContent;
for(let key in MAP) {
let tempP = paragraph.replace(key, MAP[key]);
paragraph = tempP.slice(tempP.indexOf(MAP[key]) + MAP[key].length);
newP += tempP.slice(0, tempP.indexOf(MAP[key]) + MAP[key].length);
}
document.getElementById('paragraph').innerHTML = newP + paragraph;
});
<p id="paragraph">I have a dog, a big cat, another dog, and another big cat.</p>