替换 json/jsObject/string 中的多个值



我有一个来自 Web 服务的响应,并希望将响应中的某些值替换为我的自定义值。

一种方法是编写一个树遍历器,然后检查值并替换为我的自定义值

所以回应是这样的:

[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]

现在我的自定义地图是这样的

const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};

我想要的只是

[
{
"name": "n1",
"value": "v11",
"children": [
{
"name": "n2",
"value": "v22"
}
]
},
{
"name": "n3",
"value": "v33"
}
]

我在想我是否可以字符串化我的响应,然后使用我的值映射中的自定义构建正则表达式替换值。

  1. 与树木遍历器相比,它会更快吗?
  2. 如果是,我应该怎么做?

有点像这样

originalString.replace(regexp, function (replacement))

树遍历更快

请注意,有些事情可以在正则表达式实现中更有效地完成,但我仍然认为还有一些瓶颈需要解释。

为什么正则表达式很慢:

正则表达式变慢的原因可能还有很多,但我会解释至少一个重要原因:

当您使用正则表达式查找和替换时,您每次都使用创建新字符串并每次执行匹配。正则表达式可能非常昂贵,我的实现也不是特别便宜。

为什么树遍历更快:

在树遍历中,我直接改变对象。这根本不需要创建新的string对象或任何新对象。我们也不是每次都对整个字符串执行完整搜索。

结果

运行下面的性能测试。使用console.time记录所需时间的测试。看到树遍历要快得多。

function usingRegex(obj, map) {
return JSON.parse(Object.keys(map).map(oldValue => ({
oldValue,
newValue: map[oldValue]
})).reduce((json, {
oldValue,
newValue
}) => {
return json.replace(
new RegExp(`"value":"(${oldValue})"`),
() => `"value":"${newValue}"`
);
}, JSON.stringify(obj)));
}
function usingTree(obj, map) {
function traverse(children) {
for (let item of children) {
if (item && item.value) {
// get a value from a JS object is O(1)!
item.value = map[item.value];
}
if (item && item.children) {
traverse(item.children)
}
}
}

traverse(obj);
return obj; // mutates
}
const obj = JSON.parse(`[
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
]`);
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// show that each function is working first
console.log('== TEST THE FUNCTIONS ==');
console.log('usingRegex', usingRegex(obj, map));
console.log('usingTree', usingTree(obj, map));
const iterations = 10000; // ten thousand
console.log('== DO 10000 ITERATIONS ==');
console.time('regex implementation');
for (let i = 0; i < iterations; i += 1) {
usingRegex(obj, map);
}
console.timeEnd('regex implementation');
console.time('tree implementation');
for (let i = 0; i < iterations; i += 1) {
usingTree(obj, map);
}
console.timeEnd('tree implementation');

与树遍历器相比,它会更快吗?

我不知道。我认为这将取决于输入的大小以及替换地图的大小。您可以在 JSPerf.com 运行一些测试。

如果是,我应该怎么做?

如果您要替换的值不需要任何特殊的转义或其他任何内容,那么使用基于正则表达式的字符串替换相当容易。像这样:

const input = [
{
"name": "n1",
"value": "v1",
"children": [
{
"name": "n2",
"value": "v2"
}
]
},
{
"name": "n3",
"value": "v3"
}
];
const map = {
"v1": "v11",
"v2": "v22",
"v3": "v33"
};
// create a regex that matches any of the map keys, adding ':' and quotes
// to be sure to match whole property values and not property names
const regex = new RegExp(':\s*"(' + Object.keys(map).join('|') + ')"', 'g');
// NOTE: if you've received this data as JSON then do the replacement
// *before* parsing it, don't parse it then restringify it then reparse it.
const json = JSON.stringify(input);
const result = JSON.parse(
json.replace(regex, function(m, key) { return ': "' + map[key] + '"'; })
);
console.log(result);

遍历器肯定会更快,因为字符串替换意味着针对最终字符串中的每个字符进行传输,而不是可以跳过不一定项目的迭代器。

最新更新