如何轻松测量JSON对象的复杂性



如果我想比较一系列API响应的复杂性(作为解析和验证响应可能需要花费多少精力的代理(,是否有任何现有的工具或库可以非常有效地做到这一点?还是一个简单的代码?

理想情况下,打印出一份快速报告,显示整个结构的深度和广度,以及任何其他可能有用的指标。

启发式方法是简单地计算{}[]字符的数量。当然,这只是一个启发式;在这种方法下,像{ value: "{[}{}][{{}{}]{}{}{}[}}{}{" }这样的json对象会被认为过于复杂,尽管它的结构非常简单。

let guessJsonComplexity = (json, chars='{}[]')) => {
let count = 0;
for (let char in json) if (chars.includes(char)) count++;
return count / (json.length || 1);
};

如果速度非常重要,你会选择这个答案。

如果你想要一个更简洁的答案,你几乎肯定需要解析json!

我们也可以考虑另一种方法。考虑分配一个";复杂度得分";针对json中可能发生的每一种现象。例如:

  • 包含字符串s;复杂度得分:Math.log(s.length)
  • 包括数字n;复杂度得分:Math.log(n)
  • 包含布尔值;复杂性得分:1
  • 包含一个数组;复杂度得分:元素的平均复杂度+1
  • 包含一个对象;复杂性得分:值的平均复杂性加上键的平均复杂性+1

我们甚至可以挑选出不同的关系,比如";一个对象被包括在一个数组中";,或";阵列被包括在阵列"中;,等等,如果我们想认为其中一些更";复杂的";比其他人。例如,我们可以说,负数是"负数"的两倍;复杂的";作为正数,如果这是我们的感受。

我们还可以考虑";深度因子";,这使得元素越深入就越重要。

如果我们定义了如何对所有这些现象进行评分,我们可以编写一个处理json并应用这样一个评分的函数:

let isType = (val, Cls) => val != null && val.constructor === Cls;
let getComplexity = (json, d=1.05) => {

// Here `d` is our "depth factor"

return d * (() => {
// Take the log of the length of a String
if (isType(json, String)) return Math.log(json.length);
// Take the log of (the absolute value of) any Number
if (isType(json, Number)) return Math.log(Math.abs(json));
// Booleans always have a complexity of 1
if (isType(json, Boolean)) return 1;
// Arrays are 1 + (average complexity of their child elements)
if (isType(json, Array)) {
let avg = json.reduce((o, v) => o + getComplexity(v, d), 0) / (json.length || 1);
return avg + 1;
}
// Objects are 1 + (average complexity of their keys) + (average complexity of their values)
if (isType(json, Object)) {
// `getComplexity` for Arrays will add 1 twice, so subtract 1 to compensate
return getComplexity(Object.keys(json), d) + getComplexity(Object.values(json), d) - 1;
}
throw new Error(`Couldn't get complexity for ${json.constructor.name}`);

})();

};
console.log('Simple:', getComplexity([ 'very', 'simple' ]));
console.log('Object:', getComplexity({
i: 'am',
some: 'json',
data: 'for',
testing: 'purposes'
}));
console.log('Complex:', getComplexity([
[ 111, 222, 333, 444 ],
[ 'abc', 'def', 'ghi', 'jkl' ],
[ [], [], {}, {}, 'abc', true, false ]
]));
console.log('Deep:', getComplexity([[[[[[ 'hi' ]]]]]]));

如果您想了解有关大型json对象的子对象的更多详细信息,您也可以简单地对这些子对象调用getComplexity

Im使用任意值,但这只是给您一个起点。

var data1 = { "a": { "b": 2 }, "c": [{}, {}, { "d": [1, 2, 3] }] }
var data2 = { "a": { "b": 2 }, "c": [{"x":"y","z":[0,1,2,3,4,5,6,7,8,9]}, {}, { "d": [1, 2, 3] }] }
function chkComplexity(obj) {
let complexity = 0;
let depth = 1;
(function calc(obj) {
for (const key of Object.keys(obj)) {
if (typeof obj[key] !== "object") complexity += depth
if (Array.isArray(obj)) {
depth++
complexity += depth * 2
for (const item of obj) {
calc(item)
}
}
if (typeof obj[key] === "object") {
depth++
complexity += depth * 3
calc(obj[key])
}
}
})(obj);
return complexity;
}
console.log(chkComplexity(data1));
console.log(chkComplexity(data2));

最新更新