I\'m looking for an good / elegant way to validate that a javascript object has the required properties, so far this is what I have:
var fields = [\'name\',\
If you want "elegant", what you're looking for is called a schema
:
var schema = {
name: function (value) {
return /^([A-Z][a-z\-]* )+[A-Z][a-z\-]*( \w+\.?)?$/.test(value);
},
age: function (value) {
return !isNaN(value) && parseInt(value) == value && value >= 18;
},
phone: function (value) {
return /^(\+?\d{1,2}-)?\d{3}-\d{3}-\d{4}$/.test(value);
}
};
var info = {
name: "John Doe",
age: "",
phone: "123-456-7890"
};
function validate(object, schema) {
var errors = Object.keys(schema).filter(function (key) {
return !schema[key](object[key]);
}).map(function (key) {
return new Error(key + " is invalid.");
});
if (errors.length > 0) {
errors.forEach(function (error) {
console.log(error.message);
});
} else {
console.log("info is valid");
}
}
validate(info, schema);
To address @AndreFigueiredo's pedantry, you can also check if the object
contains the property at all:
var schema = {
name: function (value) {
return /^([A-Z][a-z\-]* )+[A-Z][a-z\-]*( \w+\.?)?$/.test(value);
},
age: function (value) {
return !isNaN(value) && parseInt(value) == value && value >= 18;
},
phone: function (value) {
return /^(\+?\d{1,2}-)?\d{3}-\d{3}-\d{4}$/.test(value);
}
};
schema.name.required = true;
schema.age.required = true;
var info = {
// name: "John Doe",
age: "",
phone: "123-456-7890"
};
function validate(object, schema) {
var errors = Object.keys(schema).map(function (property) {
var validator = schema[property];
return [property, !validator.required || (property in object), validator(object[property])];
}).filter(function (entry) {
return !entry[1] || !entry[2];
}).map(function (entry) {
if (!entry[1]) return new Error(entry[0] + " is required.");
else return new Error(entry[0] + " is invalid.");
});
if (errors.length > 0) {
errors.forEach(function (error) {
console.log(error.message);
});
} else {
console.log("info is valid");
}
}
validate(info, schema);
Here's a modernized solution using features from ECMAScript 6 edition including destructuring, arrow functions, Object.entries(), template literals, and for...of:
const schema = {
name: value => /^([A-Z][a-z\-]* )+[A-Z][a-z\-]*( \w+\.?)?$/.test(value),
age: value => parseInt(value) === Number(value) && value >= 18,
phone: value => /^(\+?\d{1,2}-)?\d{3}-\d{3}-\d{4}$/.test(value)
};
let info = {
name: 'John Doe',
age: '',
phone: '123-456-7890'
};
const validate = (object, schema) => Object
.keys(schema)
.filter(key => !schema[key](object[key]))
.map(key => new Error(`${key} is invalid.`));
const errors = validate(info, schema);
if (errors.length > 0) {
for (const { message } of errors) {
console.log(message);
}
} else {
console.log('info is valid');
}
And the version that performs required
and validate
checks separately:
const schema = {
name: value => /^([A-Z][a-z\-]* )+[A-Z][a-z\-]*( \w+\.?)?$/.test(value),
age: value => parseInt(value) === Number(value) && value >= 18,
phone: value => /^(\+?\d{1,2}-)?\d{3}-\d{3}-\d{4}$/.test(value)
};
schema.name.required = true;
schema.age.required = true;
let info = {
// name: 'John Doe',
age: '',
phone: '123-456-7890'
};
const validate = (object, schema) => Object
.entries(schema)
.map(([key, validate]) => [
key,
!validate.required || (key in object),
validate(object[key])
])
.filter(([_, ...tests]) => !tests.every(Boolean))
.map(([key, invalid]) => new Error(`${key} is ${invalid ? 'invalid' : 'required'}.`));
const errors = validate(info, schema);
if (errors.length > 0) {
for (const { message } of errors) {
console.log(message);
}
} else {
console.log('info is valid');
}
I would do for a real check for empty strings, because 0
is falsy, but a value and not empty.
function validateFields(object, keys) {
keys.forEach(function (k) {
if (k in object) {
console.log(k + ": " + object[k]);
if (object[k] === '') {
console.log(k + " exists but is empty");
}
return;
}
console.log(k + " doesn't exist in object");
});
}
var fields = ['name', 'age', 'address', 'zeroString', 'zeroNumber'],
info = { name: "John Doe", age: "", phone: "123-456-7890", zeroString: '0', zeroNumber: 0 };
validateFields(info, fields);
I think that you should be more concerned about what's the proper way of checking for it, rather than the most elegant. I'll drop a link to a very good post from Todd Moto regarding this: Please, take a look
In short your code should look like this:
var validateFields = function(o, required_fields) {
required_fields.forEach(function(field){
if(field in o){
if((typeof o[field] != 'undefined')){
console.log(field + ": " + o[field]);
}else{
console.log(field + " exists but is undefined");
}
}else{
console.log(field + " doesn't exist in object");
}
});
}
Note: Take care when checking it has value, many expressions in javasscript are falsy (eg. 0, false, etc.) but are valid values.