How to separate rules array and execute functions from that array - javascript

I have a class for validation which have different validation functions.
Now what i am trying to do is make an object in another file and send all the validation data using a constructor.This constructor will receive an object that looks like this "rules[is_empty:value]". In this left part is function name and value is the value fetched from input field.Now how do I call the function and send the value as an argument.Also what should i do when there are functions that has more than one argument.
I have already tried using map method and split method but not able to access the function.
class Validator {
constructor(rules) {
let rule_obj = {}
// rules[is_empty:value,has_valid_min_length:2;3]
}
/**this is to check if the field has an empty value or not */
is_empty = (value) => {
if (value == '' || value == null) {
return true
}
else {
return false
}
}
/**this is to check if value has required minimum length or not */
has_valid_min_length = (value, min_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length < min_length) {
return false
}
else {
return true
}
}
/**this is to check if value has more characters than maximum length */
has_valid_max_length = (value, max_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length > max_length) {
return false
}
else {
return true
}
}
//this is to check if selected date is less than given limit
is_before_min_date = (value_date, min_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date < min_date) {
return true
}
else { return false }
}
//this is to check if selected date is higher than given limit
is_after_max_date = (value_date, max_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date > max_date) {
return true
}
else {
return false
}
}
}
I want to call the function which is before ':' sign in the array and give that function argument which is in value that is at the right side of ':'.
Please help.

You could send an object through the constructor...
x = {
"is_empty": [0, 2, 2],
"has_valid_min_length": [ [value, min_length], [value, min_length] ],
"has_valid_max_length": [ [value, max_length], [value, max_length] ],
"is_before_min_date": [ [value_date, min_date], [value_date, min_date] ],
"is_after_max_date": [ [value_date, max_date], [value_date, max_date] ]
}
and then in your constructor, set up a loop through the object and value arrays...
constructor(to_be_validated) {
let validator_names = Object.keys(to_be_validated);
for (let validator of validator_names) {
let values = to_be_validated[validator];
if (validator === "is_empty") {
for (let value of values) {
this.is_empty(value);
}
} else if (validator === "has_valid_min_length") {
for (let value of values) {
this.has_valid_min_length(value[0], value[1]);
}
} etc...
}
}
and then when you call the function, the methods should execute
let my_validator = new Validator(x);
I echo the comment(s) above. Wanting the approach and specific syntax of,
let my_validator = new Validator(rules[validator_name:values]);
all in one clean line like that, is a bit off. I've never seen it done like that.

You probably want an additional function in your class that calls all the check-functions according to your rules.
class Validator {
constructor(rules) {
this.rules = rules;
// rules={is_empty:[],has_valid_min_length:[2]};
}
validate = (value) => {
let pass = true;
Object.keys(this.rules).forEach(k=>pass = pass && this[k](value, ...this.rules[k]));
return pass;
}
...
}
The rules-Objects has key-value-pairs, where the keys are the function-names of the individual checks and the values are arrays of parameters that will be passed. This array can have zero or more entries.
Edit: forEach will iterate over all the keys. On every turn k will hold the current key. pass is a boolean that collects all the return values of your checker-functions. (true && false === false) That way the final return-value will be false if any of the checks returned false.
You would then apply the rules to your data like this:
myValidatorObj.validate(data);

Related

Replace multiple statements

Given the following code:
if (CONDITION1) {
if (CONDITION2) {
if (CONDITION3) {
if (CONDITION4) {
if (CONDITION5) {
} else {
return ('STOP IN CONDITION5')
}
} else {
return ('STOP IN CONDITION4')
}
} else {
return ('STOP IN CONDITION3')
}
} else {
return ('STOP IN CONDITION2')
}
} else {
return ('STOP IN CONDITION1')
}
I need to replace these multiple if statements for some other code using good practices and for cleaner reading for other people in future.
You could flatten this by testing for not condition:
function doStuff() {
if (!CONDITION1) {
return ('STOP IN CONDITION1')
}
if (!CONDITION2) {
return ('STOP IN CONDITION2')
}
if (!CONDITION3) {
return ('STOP IN CONDITION3')
}
if (!CONDITION4) {
return ('STOP IN CONDITION4')
}
if (!CONDITION5) {
return ('STOP IN CONDITION5')
}
}
Note: in between the ifs you could add code for condition true.
This has already been answered and I wouldn't recommend this version (linters probably complain, flat if statements are fine) but I'm throwing it in there to show another possibility.
You can potentially use a switch statement and add your conditions to each case:
const CONDITION1 = 1 > 2
const CONDITION2 = 2 < 1
const run = () => {
switch(true) { // always evaluate this switch
case !CONDITION1:
return 'STOP IN CONDITION1'
case !CONDITION2:
return 'STOP IN CONDITION1'
// etc...
}
}
console.log(run())
Using Array.prototype.findIndex()
Store your conditions into Array
Use Array.prototype.findIndex(). Just return the argument itself (x) => x, it fill be treated as boolean and if true the method will return the index. Store the returned integer into a variable errIndex.
If errIndex is greater than -1 that means you have an "error" in the conditions array at that index
const conditions = [false, false, true, false, false];
const errIndex = conditions.findIndex(x => x);
if (errIndex > -1) {
console.log(`STOP IN CONDITION ${errIndex + 1}`);
} else {
console.log("All passed");
}
The nice thing about using .findIndex() is in that it can short-circuit the search and immediately return the index.
Not sure why, but given you want to return a String, and since return statements are used within a function, and since it's always preferable for a function to return the same type,
you could create a function that accepts an array of conditions and a success string as the second argument:
const status = (conditions, successMsg) => {
const idx = conditions.findIndex(x=>x);
return idx < 0 ? successMsg : `STOP IN CONDITION ${idx+1}`;
};
console.log(status([false, false, true, false, false], "OK")); // STOP IN CONDITION 3
console.log(status([false, false, false, false, false], "OK")); // OK
or you can create something similar based on the above idea, like a pass function in which you do not hardcode error strings, but instead it provides you with a callback function with argument being an Object with index property or null
const pass = (conditions, fn) => {
const index = conditions.findIndex(x => x);
fn(index < 0 ? null : {index});
};
pass([false, false, true, false, false], (err) => {
if (err) {
console.log(`STOP IN CONDITION ${err.index + 1}`)
return; // Exit function here
}
// All OK...
console.log("All OK! Do some work here");
});

How to use array.prototype.reduce() with TypeScript while having a boolean accumulator as the initial value?

I was going about solving some algos on binarysearch.io and came across this problem:
I solved it using JS as follows:
class Solution {
solve(nums) {
const Hash = {};
const Cache = {};
for (let val of nums) {
if (!Hash[val]) {
Hash[val] = 0;
}
Hash[val]++; //implicit else
}
const result = Object.values(Hash).reduce((accum, curr) => {
if (accum && Cache[curr] !== 'unique') {
Cache[curr] = 'unique';
return true
}
return false;
}, true);
return result;
}
}
However, when I tried to solve it with TypeScript, which I'm fairly new to, and I had compilation errors likely caused by the .reduce() method:
class Solution {
solve(nums: Array<number>): boolean {
const Hash = {};
const Cache = {};
for (let val of nums) {
if (!Hash[val]) {
Hash[val] = 0;
}
Hash[val]++; //implicit else
}
const val_arr = Object.values(Hash);
return val_arr.reduce((accum, curr) => {
if (accum && Cache[curr] !== 'unique') {
Cache[curr] = 'unique';
return true;
}
return false;
}, true);
}
}
I'm not certain how to provide the .reduce() method a return type for the seeded initial value. I've tried changing the initial value to have a boolean type, but I still have a compilation error:
return val_arr.reduce((accum, curr) => {
if (accum && Cache[curr] !== 'unique') {
Cache[curr] = 'unique';
return true;
}
return false;
}, true as boolean);
I tried searching if anyone else has this problem, but most posts I've found have objects as the initial value, so I couldn't quite figure it out. It almost appears like the easiest solution is to solve this is to refactor to solve this without using .reduce(), but I simply want to know how to create a TS 'equivalent' to the aforementioned JS solution.
You need to
(1) Type the Hash object properly so that the type of its keys and values can be recognized by TS
(2) Type the Cache object properly so that the type of its keys and values can be recognized by TS
(3) Type the accumulator as a boolean, rather than true, so that the callback inside can return either true or false. (This can be done by passing <boolean> as the type argument to .reduce - using generics is a bit preferable to as)
class Solution {
solve(nums: Array<number>): boolean {
const Hash: {[key: number]: number} = {};
const Cache: {[key: number]: string} = {};
for (let val of nums) {
if (!Hash[val]) {
Hash[val] = 0;
}
Hash[val]++; //implicit else
}
const val_arr = Object.values(Hash);
return val_arr.reduce<boolean>((accum, curr) => {
if (accum && Cache[curr] !== 'unique') {
Cache[curr] = 'unique';
return true;
}
return false;
}, true);
}
}
Another thing to consider - JavaScript is not Java. If you have a class that just contains a single method, with no references to instance variables, it would make much more sense structurally to define a plain function instead, eg:
function solve(nums: Array<number>) {
// function implementation
}

sort function only activates after second change event

I have a function that is supposed to sort a list with objects alphabetically, depending if clicked on ascending or descending (a select with 2 options)
The problem is that when I change the value the first time, nothing happens, it only activates after the second change.
Here's my code:
function modulesSorter(a, b) {
let moduleName1 = a.module.toLowerCase();
let moduleName2 = b.module.toLowerCase();
let place = 0
if (value === "descending" || value == null) {
if (moduleName1 > moduleName2) {
place = 1
} else if (moduleName1 < moduleName2) {
place = -1
}
} else if (value === "ascending") {
if (moduleName1 > moduleName2) {
place = -1
} else if (moduleName1 < moduleName2) {
place = 1
}
}
return place;
}
let value
function changeFilter(e) {
e.preventDefault();
renderModules(modules.sort(modulesSorter));
value = document.querySelector('#sortby').value;
console.log(value)
}
I also activate the function once in the init so that it is sorted ascending as that is the default value.
It is very possible the "first" attempt sorts the objects into the same order they are current in.
The reason you see the effect you describe, is because during the first run value is undefined until after the sorting algorithm runs:
function changeFilter(e) {
e.preventDefault();
renderModules(modules.sort(modulesSorter));
value = document.querySelector('#sortby').value;
console.log(value)
}
Change the order of those two statements like this:
function changeFilter(e) {
e.preventDefault();
value = document.querySelector('#sortby').value;
renderModules(modules.sort(modulesSorter));
console.log(value)
}

Validating if object data is "true" and retrieving relevant data if so

I have a problem to solve using some data from an object. The data could take a few forms and may or may not exist in the first place. For example
things : {
oranges: true,
apples : false
}
but it could equally be:
things : {
oranges: false,
apples : false
}
or maybe things doesn't even exist
I need to:
1) Determine that things exists
2) Determine that things contains further keys
These two statements need to be verified in one callable function e.g thingsHasData()
3) If things does have data, is any of the data set to true?
This also needs to be a callable function e.g fruitsIsTrue()
4) Return the key for one of the true values
trueFruit() - this should only return one key, but it doesn't matter which (it shouldn't ever have two true values as per business rules but it's more of a fallback to just return one if for some reason it does)
So I've been able to get the key of a true key-value pair using the following:
var thingsList = {
things : {
oranges: false,
apples : true
}
}
var trueFruit = Object.keys(thingsList).filter(function(key) {
return thingsList[key];
});
return thingsList[0];
This correctly returns apples and only apples so it works for point 4 but not the others, and I feel like there is a better way to do this not having to rely on repeating the same .filter in a few different functions. Ideas?
You could take functions and for a true value, use Array#find.
function thingsHasData(object) {
return 'things' in object;
}
function fruitsIsTrue(object) {
return 'things' in object && Object.values(object.things).some(Boolean);
}
function trueFruit(object) {
return 'things' in object && Object.keys(object.things).find(k => object.things[k]);
}
var a = {},
b = { things: {} },
c = { things: { oranges: true, apples : false } },
d = { things: { oranges: false, apples : false } };
[a, b, c, d].forEach(o => console.log(
thingsHasData(o),
fruitsIsTrue(o),
trueFruit(o)
));
To check if the Object things exist, you can use the following code:
if (typeof things != "undefined") {
// It exists!
}
To check if an object has any children, check Object.keys(things).length > 0.
So the check for 1) and 2) would look like:
let things = {
oranges: true,
apples: false
}
if (typeof things != "undefined") {
// It exists!
if (Object.keys(things).length > 0) {
// It has children!
}
}
var thingsList = {
things : {
oranges: false,
apples : true
},
things2 : {
oranges: true,
apples : true
}
};
function validateThings(things) {
// (1) checks for a falsy value of things
if (!things) {
return false;
}
var keys = Object.keys(things);
// (2) checks if things has keys
if (!keys.length) {
return false;
}
// (3) then it checks for every single keys value if it is truthy
for (var i = 0, len = keys.length; i < len; i++ ) {
if (things[keys[i]]) {
// (4) return this value — all tests passed
return things[keys[i]];
}
}
return false;
}
console.log(validateThings(thingsList.notInList));
console.log(validateThings(thingsList.things));
console.log(validateThings(thingsList.things2));
const thingsHasData = arg => (arg.things && Object.keys(arg.things).length>0) ? true : false;
const trueFruit = arg => {
if (!arg.things) return;
let fruitIndex = null;
let fruitValues = Object.values(arg.things);
fruitValues.forEach((value, index) => {
if (value) fruitIndex = Object.keys(arg.things)[index];
});
return fruitIndex;
}

Object has-property-deep check in JavaScript

Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});

Categories

Resources