JS function removing old properties from object instead of adding to them - javascript

I created an object in JS called filterObj like this
var filterObj={};
The problem is that if I add property to the object, it adds but it removes the previous ones. In fact it takes the root filterObj which is empty, not the edited one.
In which I have two functions:
function handleGovernorate(Governorate) {
if (Governorate === "") {
return
}
else if (Governorate === "all") {
delete filterObj.governorate;
handleFilterObj();
return setFinalLocations(Locations);
}
else {
filterObj.governorate = Governorate;
handleFilterObj();
}
}
function handleGovernorate(District) {
if (District === "") {
return
}
else if (District === "all") {
delete filterObj.district;
handleFilterObj();
return setFinalLocations(Locations);
}
else {
filterObj.district = District;
handleFilterObj();
}
}
If I call handleGovernorate("mountLB") it sets the property governorate with value mountLB, but if I then call handleDistrict("2") the function will set a property district with value 2 but with removing the governorate property - why?

Related

How to separate rules array and execute functions from that array

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

Trying to check if object exists in Knockout Observable Array

I'm trying to check if an object has the same observable values of other objects with the same observable properties inside an observable array.
I created a foreach loop which evaluates if any of the observables match. The problem I'm having is that condition always evaluates to true, even though these values are different. I'm using typescript and knockout.
Here's the code :
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat, i) {
if (arrayOfHeats[i].MTRID() == koHeat.MTRID() && arrayOfHeats[i].HeatID() == koHeat.HeatID() && arrayOfHeats[i].PartID() == koHeat.PartID()) {
duplicate = true;
}
else
duplicate = false;
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
}
if (!c) {
return;
}
}
}
}
First problem: Your loop compares each object to itself because you re-use the variable name koHeat. I believe you really wanted to refer to the "outer" koHeat.
Second problem: You overwrite the duplicate variable in every loop iteration. This is probably not what you intend. Instead you want to stop the loop as soon as a duplicate is found.
How about something along those lines?
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
var packageId = model.mtrPackage.PackageID();
koHeat.Include = ko.observable(true);
function equals(a: MTRHeatWithInclude, b: MTRHeatWithInclude): boolean {
return a.MTRID() == b.MTRID() && a.HeatID() == b.HeatID() && a.PartID() == b.PartID();
}
if ( !!packageId && (
!model.mtrPackage.Heats().some(item => equals(item, koHeat)) ||
confirm("Warning: Duplicate MTR located on current package.Proceed ?")
)
) {
ServiceMethods.addHeatToPackage(packageId, heat.HeatID).done(() => {
model.mtrPackage.Heats.push(koHeat);
});
}
}
The equals() function should ideally be a method of the MTRHeatWithInclude class.
I think you're getting a clash between koHeat defined here:
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
And the variable defined within the forEach call. It's always returning true as (within the scope of the forEach) arrayOfHeats[i] === koHeat.
Try this:
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat2, i) {
if (koHeat2.MTRID() == koHeat.MTRID() &&
koHeat2.HeatID() == koHeat.HeatID() &&
koHeat2.PartID() == koHeat.PartID()) {
duplicate = true;
}
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
} else {
return;
}
}
}
}

Pass property with dot notation(sometimes) to function

I've got a function that is looking through a specified collection and highlighting the checkboxes for the items that are present in that collection.
function highlightFiltersPresentInTransactions(collection, collectionproperty, child) {
var found = false;
angular.forEach(collection, function (filterType) {
if (scope.vm.transactions) {
found = scope.vm.transactions.filter(function (obj) {
if (child) {
return obj[collectionproperty][child] === filterType.name;
} else {
return obj[collectionproperty] === filterType.name;
}
});
}
if (found) {
filterType['has-transaction'] = (found.length > 0);
}
});
}
I'm able to call it and it correctly works like this
highlightFiltersPresentInTransactions(scope.filterTypes, 'target', 'type');
highlightFiltersPresentInTransactions(scope.actionTypes, 'transactionType');
What I would like to be able to avoid is the check whether there is a child element that needs to be checked.
I attempted to call the function as such:
highlightFiltersPresentInTransactions(scope.filterTypes, 'target.type');
Since this is a string it doesn't find the property. I also tried creating a blank target object then passing target.type without the quotes.
How can I dynamically pass in a property that might or might not have a child property to my function?
How about passing a function reference to the function?
highlightFiltersPresentInTransactions(scope.filterTypes, function(o) { return o.target.type; });
highlightFiltersPresentInTransactions(scope.filterTypes, function(o) { return o.transactionType; });
This can be implemented pretty easily:
function highlightFiltersPresentInTransactions(collection, readFn) {
var found = false;
angular.forEach(collection, function (filterType) {
if (scope.vm.transactions) {
found = scope.vm.transactions.filter(function (obj) {
return readFn(obj) === filterType.name;
});
}
if (found) {
filterType['has-transaction'] = (found.length > 0);
}
});
}
If you don't want to do that, one way or another you'll have to split your string target.type into separate properties, and do it your existing way (just without the explicit parameter for child).

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

How to accomplish this without using eval

Sorry for the title but I don't know how to explain it.
The function takes an URI, eg: /foo/bar/1293. The object will, in case it exists, be stored in an object looking like {foo: { bar: { 1293: 'content...' }}}. The function iterates through the directories in the URI and checks that the path isn't undefined and meanwhile builds up a string with the code that later on gets called using eval(). The string containing the code will look something like delete memory["foo"]["bar"]["1293"]
Is there any other way I can accomplish this? Maybe store the saved content in something other than
an ordinary object?
remove : function(uri) {
if(uri == '/') {
this.flush();
return true;
}
else {
var parts = trimSlashes(uri).split('/'),
memRef = memory,
found = true,
evalCode = 'delete memory';
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
memRef = memRef[dir];
evalCode += '["'+dir+'"]';
}
else {
found = false;
return false;
}
if(i == (parts.length - 1)) {
try {
eval( evalCode );
} catch(e) {
console.log(e);
found = false;
}
}
});
return found;
}
}
No need for eval here. Just drill down like you are and delete the property at the end:
parts.forEach(function(dir, i) {
if( memRef[dir] !== undefined ) {
if(i == (parts.length - 1)) {
// delete it on the last iteration
delete memRef[dir];
} else {
// drill down
memRef = memRef[dir];
}
} else {
found = false;
return false;
}
});
You just need a helper function which takes a Array and a object and does:
function delete_helper(obj, path) {
for(var i = 0, l=path.length-1; i<l; i++) {
obj = obj[path[i]];
}
delete obj[path.length-1];
}
and instead of building up a code string, append the names to a Array and then call this instead of the eval. This code assumes that the checks to whether the path exists have already been done as they would be in that usage.

Categories

Resources