首页 > 解决方案 > 如何轻松测量 JSON 对象的复杂性?

问题描述

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

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

标签: javascriptjson

解决方案


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

let guessJsonComplexity = (json, chars=new Set('{}[]'.split(''))) => {
  let count = 0;
  for (let char in json) if (chars.has(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这些子对象。


推荐阅读