如何检查对象中的所有数字是否都在一定范围内?
How to check if all numbers in an object are within a certain range?
我想在当前 JS/jQuery 项目中加入的功能之一是允许用户导出和导入他们保存的数据。 import/export 数据只是从 运行 到 JSON.stringify 和 JSON.parse 的一个大对象,实现它的基本版本效果很好,但我想验证导入数据以确保它能正常工作(这不是关于作为 JavaScript 工作,而是更多关于在我正在制作的游戏规则范围内工作)。
我想想出一个更有效的脚本来验证这个大而复杂的对象。我唯一关心的对象部分是数字,但它还包括字符串、数组、嵌套对象。一些嵌套对象包括我关心的数字,但也包括更多其他垃圾,包括更进一步的嵌套对象。目前,我正在使用 jQuery 的 each() 函数来遍历对象的每个子部分并对其求值,但这需要我为我关心的每个子部分编写一个 each 函数。
如何将整个对象提供给一个函数,并让它只查看数字以查看它们是大于还是小于设定值?
编辑:将对象粘贴到这里,目的是检查是否所有数字。有些需要在0到3之间,有些需要在0到9之间。
{
"meta": {
"id": 2,
"name": "Test",
"concept": "test description"
},
"coreAttributes": {
"Strength": 3,
"Finesse": 1,
"Perception": 9,
"Stamina": 1,
"Agility": 1,
"Wits": 1,
"Willpower": 1,
"Charisma": 1,
"Intelligence": 1
},
"skills": {
"Toughness": ["Strength", "STR", 0],
"Stealth": ["Finesse", "FIN", 0],
"Alertness": ["Perception", "PER", 3],
"Investigate": ["Perception", "PER", 0],
"Athletics": ["Agility", "AGI", 0],
"Drive": ["Wits", "WIT", 0],
"Survival": ["Wits", "WIT", 0],
"Guts": ["Willpower", "WIL", 4],
"Hardware": ["Intelligence", "INT", 0],
"Humanities": ["Intelligence", "INT", 0],
"Medicine": ["Intelligence", "INT", 0],
"Science": ["Intelligence", "INT", 0],
"Software": ["Intelligence", "INT", 0],
"Charm": ["Charisma", "CHA", 0],
"Manipulate": ["Charisma", "CHA", 0]
},
"unrolledSkills": {
"Contacts": 0,
"Languages": 0,
"Resources": 0
},
"combatSkills": {
"Unarmed": ["Strength", "STR", 0],
"Defense": ["Finesse", "FIN", 0],
"Melee": ["Finesse", "FIN", 0],
"Firearms": ["Perception", "PER", 0],
"Ballistics": ["Perception", "PER", 0],
"Initiative": ["Wits", "WIT", 0]
},
"attacks": {},
"status": {
"currentEndurance": 4,
"currentSpeed": 4,
"currentEntanglement": 4,
"body": {
"upper": {
"wounds": 0,
"armor": ["", 0]
},
"lower": {
"wounds": 0,
"armor": ["", 0]
},
"main": {
"wounds": 0,
"armor": ["", 0]
},
"off": {
"wounds": 0,
"armor": ["", 0]
},
"legs": {
"wounds": 0,
"armor": ["", 0]
}
}
},
"styles": {
"classes": {
"Strength": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Finesse": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Perception": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Stamina": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Agility": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Wits": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Willpower": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Charisma": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Intelligence": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
}
},
"arcane": {
"restoration": 0,
"evocation": 0,
"abjuration": 0,
"sublimation": 0,
"paradigm": 0,
"telepathy": 0,
"shift": 0,
"electromagnetism": 0,
"gravitonertia": 0,
"chromodynamism": 0,
"technology": 0
},
"extension": {
"avatar": 0,
"proxy": 0,
"permanence": 0
}
},
"addenda": {}
}
你要找的其实很简单——递归。
下面的代码检查是否所有数字都大于或等于 2,并且在第一个不匹配的数字上 return false。
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
var recurse = function(a) {
var s = true;
for (prop in a) {
switch (typeof a[prop]) {
case "number":
//Check for conditions here
console.log("found number " + a[prop]);
if (a[prop] < 2) {
return false;
}
break;
case "object":
if (a[prop] !== null) {
s = s && recurse(a[prop]);
}
break;
default:
break;
}
}
return s;
}
recurse(x);
这是一个迭代解决方案:
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
function check (obj) {
var queue = [obj];
while (queue.length > 0) {
var current = queue.shift();
for (var prop in current) {
if (!current.hasOwnProperty(prop)) continue;
switch (typeof(current[prop])) {
case "number":
// check for conditions here
if (current[prop] < 2) {
return false;
}
break;
case "object":
if (current[prop] !== null) {
queue.push(current[prop]);
}
break;
default:
break;
}
}
}
return true;
}
alert(check(x));
因为可以提早完成所以速度会快一些
下面使用递归来搜索多维对象中的所有键,以查找小于或大于提供的数字的任何数字。
只需调用 checkObject(myStuff, 30, 60);
之类的函数,传入您的对象、允许的最低数字和允许的最高数字
var myStuff = { "results": [
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "56",
"passingPoints": "34",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "59",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "57"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "44",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "87",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "79",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "76",
"userPoints": "20",
"userRightAnswers": "45"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "19",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "21",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "20"
}
] };
// track how many invalid numbers we find
var hasInvalidData=0;
// call our checkObject() function, pass it
// your object, your lowest allowed number, your highest allowed number
checkObject(myStuff, 30, 60);
if(hasInvalidData > 0){
alert(hasInvalidData + ' invalid numbers were found')
}
function checkObject(object, low, high){
// loop through each property of the object
for (var property in object) {
// make sure it's a real property and not inherited
if (object.hasOwnProperty(property)) {
//get the value of the current property
var value = object[property];
// if this propery is itself an object,
// call this function recursively
if(typeof object[property] == "object" && typeof object[property] !== null){
checkObject(object[property], low, high)
}
else{
// if it's not an object
// check if it a a number and not true or false
// which wihich isNaN sees as 1 and 0
if( !isNaN(value) && typeof value != "boolean"){
console.log(value);
if(value < low){
console.log('^ this value is too small ^');
hasInvalidData++;
}
if(value > high){
console.log('^ this value is too large ^');
hasInvalidData++;
}
}
}
}
}
}
给你,你只需要这个:(它还有一些奇特的可视化:P)
function traverse(obj, callback, level) {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
callback.apply(this, [i, obj[i], level||""]);
(obj[i] !== null && typeof(obj[i])=="object") && traverse(obj[i],callback,(level||"")+" ");
}
}
// it is called like this
traverse(obj, valueCheck);
//obj - is your object as in your question
//valueCheck(key, val, level) - is your own function to check the value
示例性的 valueCheck()
函数可能如下所示
function valueCheck(key, val, level) {
// proceed to check only if the value is a number
if (!isNaN(parseFloat(val)) && isFinite(val)) {
var isError = ! ((0 <= val) && (val <= 9));
console.log(level + key + " : " + val + (isError && " ----------------------- VALUE ERROR" || ""));
} else {
// if it's a new object, print shiny "-" at the begining
(typeof val == "object") && console.log(Array(1+level.length).join("-") + key);
}
}
我想在当前 JS/jQuery 项目中加入的功能之一是允许用户导出和导入他们保存的数据。 import/export 数据只是从 运行 到 JSON.stringify 和 JSON.parse 的一个大对象,实现它的基本版本效果很好,但我想验证导入数据以确保它能正常工作(这不是关于作为 JavaScript 工作,而是更多关于在我正在制作的游戏规则范围内工作)。
我想想出一个更有效的脚本来验证这个大而复杂的对象。我唯一关心的对象部分是数字,但它还包括字符串、数组、嵌套对象。一些嵌套对象包括我关心的数字,但也包括更多其他垃圾,包括更进一步的嵌套对象。目前,我正在使用 jQuery 的 each() 函数来遍历对象的每个子部分并对其求值,但这需要我为我关心的每个子部分编写一个 each 函数。
如何将整个对象提供给一个函数,并让它只查看数字以查看它们是大于还是小于设定值?
编辑:将对象粘贴到这里,目的是检查是否所有数字。有些需要在0到3之间,有些需要在0到9之间。
{
"meta": {
"id": 2,
"name": "Test",
"concept": "test description"
},
"coreAttributes": {
"Strength": 3,
"Finesse": 1,
"Perception": 9,
"Stamina": 1,
"Agility": 1,
"Wits": 1,
"Willpower": 1,
"Charisma": 1,
"Intelligence": 1
},
"skills": {
"Toughness": ["Strength", "STR", 0],
"Stealth": ["Finesse", "FIN", 0],
"Alertness": ["Perception", "PER", 3],
"Investigate": ["Perception", "PER", 0],
"Athletics": ["Agility", "AGI", 0],
"Drive": ["Wits", "WIT", 0],
"Survival": ["Wits", "WIT", 0],
"Guts": ["Willpower", "WIL", 4],
"Hardware": ["Intelligence", "INT", 0],
"Humanities": ["Intelligence", "INT", 0],
"Medicine": ["Intelligence", "INT", 0],
"Science": ["Intelligence", "INT", 0],
"Software": ["Intelligence", "INT", 0],
"Charm": ["Charisma", "CHA", 0],
"Manipulate": ["Charisma", "CHA", 0]
},
"unrolledSkills": {
"Contacts": 0,
"Languages": 0,
"Resources": 0
},
"combatSkills": {
"Unarmed": ["Strength", "STR", 0],
"Defense": ["Finesse", "FIN", 0],
"Melee": ["Finesse", "FIN", 0],
"Firearms": ["Perception", "PER", 0],
"Ballistics": ["Perception", "PER", 0],
"Initiative": ["Wits", "WIT", 0]
},
"attacks": {},
"status": {
"currentEndurance": 4,
"currentSpeed": 4,
"currentEntanglement": 4,
"body": {
"upper": {
"wounds": 0,
"armor": ["", 0]
},
"lower": {
"wounds": 0,
"armor": ["", 0]
},
"main": {
"wounds": 0,
"armor": ["", 0]
},
"off": {
"wounds": 0,
"armor": ["", 0]
},
"legs": {
"wounds": 0,
"armor": ["", 0]
}
}
},
"styles": {
"classes": {
"Strength": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Finesse": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Perception": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Stamina": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Agility": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Wits": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Willpower": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Charisma": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
},
"Intelligence": {
"core": 0,
"spec1": 0,
"spec2": 0,
"spec3": 0,
"aux1": {
"skill": false,
"name": "",
"value": 0
},
"aux2": {
"skill": false,
"name": "",
"value": 0
},
"aux3": {
"skill": false,
"name": "",
"value": 0
}
}
},
"arcane": {
"restoration": 0,
"evocation": 0,
"abjuration": 0,
"sublimation": 0,
"paradigm": 0,
"telepathy": 0,
"shift": 0,
"electromagnetism": 0,
"gravitonertia": 0,
"chromodynamism": 0,
"technology": 0
},
"extension": {
"avatar": 0,
"proxy": 0,
"permanence": 0
}
},
"addenda": {}
}
你要找的其实很简单——递归。
下面的代码检查是否所有数字都大于或等于 2,并且在第一个不匹配的数字上 return false。
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
var recurse = function(a) {
var s = true;
for (prop in a) {
switch (typeof a[prop]) {
case "number":
//Check for conditions here
console.log("found number " + a[prop]);
if (a[prop] < 2) {
return false;
}
break;
case "object":
if (a[prop] !== null) {
s = s && recurse(a[prop]);
}
break;
default:
break;
}
}
return s;
}
recurse(x);
这是一个迭代解决方案:
var x = {
b: 5,
c: {
d: 3,
e: [1, 2, 3],
f: function() {}
},
g: function() {}
};
function check (obj) {
var queue = [obj];
while (queue.length > 0) {
var current = queue.shift();
for (var prop in current) {
if (!current.hasOwnProperty(prop)) continue;
switch (typeof(current[prop])) {
case "number":
// check for conditions here
if (current[prop] < 2) {
return false;
}
break;
case "object":
if (current[prop] !== null) {
queue.push(current[prop]);
}
break;
default:
break;
}
}
}
return true;
}
alert(check(x));
因为可以提早完成所以速度会快一些
下面使用递归来搜索多维对象中的所有键,以查找小于或大于提供的数字的任何数字。
只需调用 checkObject(myStuff, 30, 60);
之类的函数,传入您的对象、允许的最低数字和允许的最高数字
var myStuff = { "results": [
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "56",
"passingPoints": "34",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "59",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "57"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "44",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "87",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "79",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "76",
"userPoints": "20",
"userRightAnswers": "45"
},
{
"ACL": {
"7UeILO5tC4": {
"count": "45",
"read": true
},
"role:Leads": {
"count": "12",
"read": true,
"write": true
}
},
"createdAt": "2014-12-16T22:04:46.338Z",
"finishDate": "12%2F16%2F2014",
"finishTime": "16%3A4%3A44",
"objectId": "tVldoxxdCB",
"passFail": "Pass",
"passingPercentage": "90",
"passingPoints": "19",
"questions": "21",
"quizName": "Name",
"quizType": "Flights",
"teamMember": "Jame Fellows",
"ttlPossiblePoints": "21",
"updatedAt": "2014-12-16T22:04:46.338Z",
"userName": "Jame.Fellows",
"userPercentage": "95",
"userPoints": "20",
"userRightAnswers": "20"
}
] };
// track how many invalid numbers we find
var hasInvalidData=0;
// call our checkObject() function, pass it
// your object, your lowest allowed number, your highest allowed number
checkObject(myStuff, 30, 60);
if(hasInvalidData > 0){
alert(hasInvalidData + ' invalid numbers were found')
}
function checkObject(object, low, high){
// loop through each property of the object
for (var property in object) {
// make sure it's a real property and not inherited
if (object.hasOwnProperty(property)) {
//get the value of the current property
var value = object[property];
// if this propery is itself an object,
// call this function recursively
if(typeof object[property] == "object" && typeof object[property] !== null){
checkObject(object[property], low, high)
}
else{
// if it's not an object
// check if it a a number and not true or false
// which wihich isNaN sees as 1 and 0
if( !isNaN(value) && typeof value != "boolean"){
console.log(value);
if(value < low){
console.log('^ this value is too small ^');
hasInvalidData++;
}
if(value > high){
console.log('^ this value is too large ^');
hasInvalidData++;
}
}
}
}
}
}
给你,你只需要这个:(它还有一些奇特的可视化:P)
function traverse(obj, callback, level) {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
callback.apply(this, [i, obj[i], level||""]);
(obj[i] !== null && typeof(obj[i])=="object") && traverse(obj[i],callback,(level||"")+" ");
}
}
// it is called like this
traverse(obj, valueCheck);
//obj - is your object as in your question
//valueCheck(key, val, level) - is your own function to check the value
示例性的 valueCheck()
函数可能如下所示
function valueCheck(key, val, level) {
// proceed to check only if the value is a number
if (!isNaN(parseFloat(val)) && isFinite(val)) {
var isError = ! ((0 <= val) && (val <= 9));
console.log(level + key + " : " + val + (isError && " ----------------------- VALUE ERROR" || ""));
} else {
// if it's a new object, print shiny "-" at the begining
(typeof val == "object") && console.log(Array(1+level.length).join("-") + key);
}
}