我正在为一个大型应用程序使用angular-translate。有几个人提交代码+翻译,很多时候翻译对象不同步。
我正在构建一个Grunt插件来查看这两个文件'结构并比较它(只是键和整体结构,而不是值)。
主要目标是:
事实证明它比我预期的要复杂一点。所以我想我可以做类似的事情:
一个很小的例子是以下对象:
{
key1: 'cool',
key2: 'cooler',
keyWhatever: {
anotherObject: {
key1: 'better',
keyX: 'awesome'
},
aObject: 'actually, it\'s a string'
},
aKey: 'more awesomeness'
}
将映射到:
{
aKey: 'String',
key1: 'String',
key2: 'String',
keyWhatever: {
aObject: 'String',
anotherObject: {
key1: 'String',
keyX: 'String'
}
}
}
在此之后,我会对所有对象进行字符串化并进行严格的比较。
我的问题是,有更好的方法来执行此操作吗?在简单性和性能方面,因为有很多翻译文件,而且它们相当大。
我试图找到已经这样做的图书馆,但我找不到。
谢谢
编辑:感谢Jared指出对象无法排序。我很惭愧这样说:D另一个解决方案可能是迭代主翻译文件中的每个属性,如果它们是字符串,则将密钥与其他文件进行比较。如果它们是对象,"输入"他们,并做同样的事情。也许它比我的第一次猜测更简单。该怎么办?
答案 0 :(得分:2)
假设你有两个JSON对象,jsonA和jsonB。
function compareValues(a, b) {
//if a and b aren't the same type, they can't be equal
if (typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
var keysA = Object.keys(a).sort(),
keysB = Object.keys(b).sort();
//if a and b are objects with different no of keys, unequal
if (keysA.length !== keysB.length) {
return false;
}
//if keys aren't all the same, unequal
if (!keysA.every(function(k, i) { return k === keysB[i];})) {
return false;
}
//recurse on the values for each key
return keysA.every(function(key) {
//if we made it here, they have identical keys
return compareValues(a[key], b[key]);
});
//for primitives just use a straight up check
} else {
return a === b;
}
}
//true if their structure, values, and keys are identical
var passed = compareValues(jsonA, jsonB);
请注意,对于深层嵌套的JSON对象,这可能会溢出堆栈。另请注意,这将适用于JSON,但不一定是常规JS对象,因为日期对象,正则表达式等需要特殊处理。
答案 1 :(得分:1)
实际上,您确实需要对键进行排序,因为它们不需要以任何特定顺序吐出。写一个函数,
function getComparableForObject(obj) {
var keys = Object.keys(obj);
keys.sort(a, b => a > b ? 1 : -1);
var comparable = keys.map(
key => key + ":" + getValueRepresentation(obj[key])
).join(",");
return "{" + comparable + "}";
}
其中getValueRepresentation
是一个返回“String”或调用getComparableForObject
的函数。如果您担心循环引用,请将Symbol
添加到外部作用域repr
,在上面的函数中指定obj[repr] = comparable
,并在getValueRepresentation
中检查已定义的每个对象obj[repr]
并返回它而不是递归处理它。
答案 2 :(得分:1)
从对象中对键的数组进行排序。但是,排序的平均time complexity为O(n⋅log(n))
。我们可以做得更好。确保两组 A 和 B 的快速通用算法等价如下:
for item in B
if item in A
remove item from A
else
sets are not equivalent
sets are equivalent iff A is empty
为了解决@ Katana31,我们可以通过维护一组访问对象并确保该对象的所有后代都不在列表中来检测循环引用:
# poorly written pseudo-code
fn detectCycles(A, found = {})
if A in found
there is a cycle
else
found = clone(found)
add A to found
for child in A
detectCycles(child, found)
这是一个完整的实现(您可以找到一个假定JSON /非循环输入here的简化版本):
var hasOwn = Object.prototype.hasOwnProperty;
var indexOf = Array.prototype.indexOf;
function isObjectEmpty(obj) {
for (var key in obj) {
return false;
}
return true;
}
function copyKeys(obj) {
var newObj = {};
for (var key in obj) {
newObj[key] = undefined;
}
return newObj;
}
// compares the structure of arbitrary values
function compareObjectStructure(a, b) {
return function innerCompare(a, b, pathA, pathB) {
if (typeof a !== typeof b) {
return false;
}
if (typeof a === 'object') {
// both or neither, but not mismatched
if (Array.isArray(a) !== Array.isArray(b)) {
return false;
}
if (indexOf.call(pathA, a) !== -1 || indexOf.call(pathB, b) !== -1) {
return false;
}
pathA = pathA.slice();
pathA.push(a);
pathB = pathB.slice();
pathB.push(b);
if (Array.isArray(a)) {
// can't compare structure in array if we don't have items in both
if (!a.length || !b.length) {
return true;
}
for (var i = 1; i < a.length; i++) {
if (!innerCompare(a[0], a[i], pathA, pathA)) {
return false;
}
}
for (var i = 0; i < b.length; i++) {
if (!innerCompare(a[0], b[i], pathA, pathB)) {
return false;
}
}
return true;
}
var map = copyKeys(a), keys = Object.keys(b);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!hasOwn.call(map, key) || !innerCompare(a[key], b[key], pathA,
pathB)) {
return false;
}
delete map[key];
}
// we should've found all the keys in the map
return isObjectEmpty(map);
}
return true;
}(a, b, [], []);
}
请注意,此实现直接比较两个对象的结构等效性,但不会将对象减少为直接可比较的值(如字符串)。我没有做任何性能测试,但我怀疑它不会增加重要的价值,虽然它将消除重复确保对象是非循环的需要。因此,您可以轻松地将compareObjectStructure拆分为两个函数 - 一个用于比较结构,另一个用于检查周期。