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','age','address'];
var info = {
name: "John Doe",
age: "",
phone: "123-456-7890"
}
var validateFields = function(o, required_fields) {
required_fields.forEach(function(field){
if(o.hasOwnProperty(field)){
if(o[field]){
console.log(field + ": " + o[field]);
}else{
console.log(field + " exists but is empty");
}
}else{
console.log(field + " doesn't exist in object");
}
});
}
validateFields(info, fields);
Is there a more efficient / elegant way of doing this in plain javascript?
EDIT: Ok so I'm glad I asked because I had completely missed a bunch of possible conditions like zero.
With elegance out the window, how is this for a validation function? Are there any other cases I should be checking for?
var fields = ['name','age','address'];
var info = {
name: "John Doe",
age: 0,
address: false,
phone: "123-456-7890"
}
var validateFields = function(o, required_fields, null_valid, zero_valid, empty_valid) {
var invalid_fields = [];
required_fields.forEach(function(field){
if(field in o){
switch(o[field]){
case '':
console.log(field + " exists but is empty");
if(!empty_valid){
invalid_fields.push(o[field]);
}
break;
case undefined:
console.log(field + " exists but is undefined");
invalid_fields.push(o[field]);
break;
case null:
console.log(field + " exists but is null");
if(!null_valid){
invalid_fields.push(o[field]);
}
break;
case 0:
console.log(field + " exists and the value is 0");
if(!zero_valid){
}
invalid_fields.push(o[field]);
break;
default:
console.log(field + ": " + o[field]);
break;
}
}else{
console.log(field + " doesn't exist in object");
invalid_fields.push(o[field]);
}
});
return invalid_fields;
}
var invalid = validateFields(info, fields, true, true, false);
console.log(invalid);
if(invalid.length >0){
console.log("ERROR: Missing fields");
}else{
console.log("Fields are valid");
}
You can use the hasOwnProperty () method or in operator or Compare with undefined to check if the object has property in JavaScript. The hasOwnProperty () method returns true if a property exists in an object: Simple example codes to check for property existence in a JavaScript object.
Unlike the in operator, hasOwnProperty does not check for a property in the object's prototype chain. If an object is an Array, this method can check whether an index exists.
'myProp' in object also determines whether myProp property exists in object. Let's use in operator to detect the existence of name and realName in hero object: 'name' in hero evaluates to true because hero has a property name. On the other side, 'realName' in hero evaluates to false because hero doesn't have a property named 'realName'.
hasOwnProperty () searches only within the own properties of the object. The second approach makes use of propName in object operator. The operator evaluates to true for an existing property, and false otherwise. in operator looks for properties existence in both own and inherited properties.
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');
}
You can use https://github.com/jquense/yup to solve your problem.
import * as yup from 'yup';
let schema = yup.object().shape({
name: yup.string().required(),
age: yup.number().required().positive().integer(),
address: yup.string().required(),
});
var info = {
name: "John Doe",
age: "",
phone: "123-456-7890"
}
// check validity
schema
.isValid(info)
.then(function (valid) {
valid; // => false
});
This is doing a bit more than just checking for the existence of fields, it's also ensuring that the data type is correct e.g. that age is a non-negative number.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With