Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript - elegant way to check object has required properties

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");
}
like image 584
Mankind1023 Avatar asked Jul 27 '16 14:07

Mankind1023


People also ask

How to check if an object has property in JavaScript?

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.

What does hasOwnProperty check in JavaScript?

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.

How to check if myprop property exists in a hero object?

'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'.

How to find if a property exists in an object?

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.


2 Answers

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);

Update

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');
}
like image 54
Patrick Roberts Avatar answered Oct 22 '22 18:10

Patrick Roberts


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.

like image 29
Jamie McCrindle Avatar answered Oct 22 '22 19:10

Jamie McCrindle