Pass property with dot notation(sometimes) to function - javascript

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

Related

passing an array to a function but logs as undefined

I have 2 event listeners and they both push the element that is clicked into 2 separate arrays. These console.log in the event listener functions as expected, however when I pass them into a move function they both return undefined.
I've tried it with just passing the element that is selected into the function as well with the same result.
document.querySelectorAll('img').forEach(function(el) {
el.addEventListener('click' , function(params) {
el.classList.add('selected')
selectedPieces.push(el)
console.log(selectedPieces)
moveBy(selectedPieces)
})
})
document.querySelectorAll('.square').forEach(function(el) {
el.addEventListener('click', function() {
el.classList.add('target')
targetPieces.push(el)
console.log(targetPieces)
moveBy(targetPieces)
})
})
function moveBy(selected,target) {
console.log(selected)
console.log(target)
}
So I did create a work around in this. It involved sending the addEventListeners to their own functions, then adding them to an array, then sending that array to it's own function which then allowed me to move things around.
Only thing wrong with this version is that it only works one time, so I'm currently working through what is causing it to only fire once.
document.querySelectorAll('img').forEach(function(el) {
el.addEventListener('click' , addselected)
})
document.querySelectorAll('.square').forEach(function(el) {
el.addEventListener('click', addtarget)
})
function addselected(selected){
console.log('---addselected---')
var select = selected.path[0]
moveList.push(select)
}
function addtarget(target){
console.log('---addtarget---')
if (target.path.length === 7){
var tar = target.path[1]
}else{
var tar = target.path[0]
}
moveList.push(tar)
moveBy(...moveList)
}
function moveBy(...moveList) {
console.log('---moveBy---')
let moveTarg = moveList[2]
let moveSel = moveList[0]
if (typeof moveTarg === 'undefined') {
console.log('not working')
}else{
moveTarg.appendChild(moveSel)
moveList = []
}
console.log(moveList)
}
well
function moveBy(selected,target) {
console.log(selected)
console.log(target)
}
has two paramaters.
moveBy(targetPieces)
but you pass only one parameter in.

How to use recursion in JavaScript for try do function?

I want to make this code prettier with recursion.
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
try {
do {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
} while (i < modelStyles.length);
return model;
} catch (e) {
return undefined;
}
};
tried this:
findModel = function(oldModel, ...modelStyles) {
let model = oldModel.elements;
let i = 0;
if (i < modelStyles.length) {
model = model.children.find(child => child.mStyle === modelStyles[i]);
i += 1;
return model;
} else {
return undefined;
}
};
but it's still not working well. in the first code I get only the element, in the second one I get also undefined.
What did I wrong?
As amply noted in comments, you are actually never calling the function recursively.
When it comes to "pretty", I would not go for recursion, but for reduce:
var findModel = function(oldModel, ...modelStyles) {
try {
return modelStyles.reduce((model, style) => model.children.find(child => child.mStyle === style), oldModel.elements);
} catch (e) {} // No need to explicitly return undefined. It is the default
};
If you really need recursion, then first realise that your function expects a first argument type that never occurs again. Only the toplevel model has an elements property, so you can only call this function for ... the top level of your hierarchy.
To make it work, you would need another function that takes the model type as it occurs in the children:
var findModel = function(oldModel, ...modelStyles) {
function recur(model, style, ...modelStyles) {
if (style === undefined) return model;
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
}
// Need to change the type of the first argument:
try {
return recur(oldModel.elements, ...modelStyles);
} catch (e) {}
};
If you would change the code where the function is called initially, you could of course pass mainmodel.elements instead of mainmodel, so that this type difference problem is resolved. If you can make that change, then the recursive function can become:
var findModel = function(model, style, ...modelStyles) {
if (style === undefined) return model;
try {
return recur(model.children.find(child => child.mStyle === style), ...modelStyles);
} catch (e) {}
};
Still, I would prefer the reduce variant.
The point of recursive function is to call themselves into themselves. In your case, you are calling the function once, but the function never call itself so it just go through once. I'm not sure of the context so I can't fix your code but i can give you an example of recursion.
Lets say we have an object with property. Some are string, some are number and some are objects. If you want to retrieve each key of this object you would need recursion, since you don't know how deep the object goes.
let objectToParse = {
id: 10,
title: 'test',
parent: {
id: 5,
title: 'parent',
someKey: 3,
parent: {
id: 1,
title: 'grand-parent',
parent: null,
someOtherkey: 43
}
}
};
function parseParentKey(object) {
let returnedKey = [];
let ObjectKeys = Object.keys(object);
for(let i = 0; i < ObjectKeys.length; i++) {
if(typeof object[ObjectKeys[i]] === "object" && object[ObjectKeys[i]] !== null) {
// we are calling the methode inside itself because
//the current property is an object.
returnedKey = returnedKey.concat(parseParentKey(object[ObjectKeys[i]]));
}
returnedKey.push(ObjectKeys[i]);
}
return returnedKey;
}
console.log(parseParentKey(objectToParse));
I know this does not answer your question but it gives you a hint on how to use recursion properly. If your first code works, I don't see why you would need to change it in the first place.

javascript - unused argument 'idtype' although called from a .map() function

I am trying to re-write a function that filters out a specific property of an object to a function that can be passed a property and filter it.
This is the initial function:
function filterCategory(xmlObject, id) {
let newData = [];
xmlObject
.Sports[0]
.Sport[0]
.Category
.map(function (category) {
if (category.$.CategoryID == id) {
newData.push(category);
}
});
xmlObject
.Sports[0]
.Sport[0]
.Category = newData;
return xmlObject;
}
This is my new function:
function filterProperty(xmlObject, property, idtype, id) {
let newData = [];
if(xmlObject.hasOwnProperty(property)) {
xmlObject.property.map(function(value) {
if(value.$.idtype == id) {
newData.push(value);
}
});
xmlObject.property = newData;
}
return xmlObject;
}
For the second function my linter returns Unused idtype. Will my function be able to access the argument, or will it fail because I am trying to call it from a map() function? If so, how can I avoid this?
If you want to use idtype as a dynamic object property, then you can't use it like my.object.idtype as that will look for the property on the object that is literally called "idtype", instead you can use bracket notation to access the property
value.$[idtype];
Further illustration:
var obj = { one: 1, two: 2, three: 'foobarbaz' };
function getThingFromObject(mything) {
return obj[mything];
}
console.log(getThingFromObject('one')); // 1
console.log(getThingFromObject('three')); // 'foobarbaz'

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

Replace value with API value in ng-repeat

I am listing a bunch of JSON objects to a view, but only it's category ID is included. Not the name, which I need to display. I have to make a separate $http call to process which items match. Values will not render to view. Code:
$scope.cardCategories = function(id) {
angular.forEach($scope.categoryFilter, function(category, index){
if (category.id == id) {
//console.log(category.name);
return category.name;
} else {
return;
}
})
}
value inside a simplified ng-repeat
<div ng-repeat="topic in latest">
{{cardCategories(topic.category_id)}}
</div>
I have also attempted to write this as a filter, but the value will not display. Console shows the matches. My suspicion is that I have to process the original latest array. Thanks.
.filter('cardCategories', function($rootScope, $state){
return function(catId) {
if (catId !== undefined && $rootScope.cardCategories) {
angular.forEach($rootScope.cardCategories.categories, function(category, index){
if (category.id == catId.category_id) {
return category.name;
} else {
return;
}
})
}
}
})
View:
{{topic.category_id | cardCategories}}
That return statement in the forEach callback function will not return a value from the $scope.cardCategories, it returns something from the callback you provided to forEach (and angular happily ignores it). Try something like this:
$scope.cardCategories = function(id) {
var i = $scope.categoryFilter.length; // assuming it's an array
while(i--) {
if($scope.categoryFilter[i].id === id) {
return $scope.categoryFilter[i].name;
}
}
};
Also -- there's no way to break (stop early) an angular.forEach loop, so for performance reasons it's better to avoid it if you're locating something inside an array/object.
You need to make sure the forEach loop doesn't do a return if the current iteration doesn't match the ID you passed in.
Take out the else condition from your if statement
$scope.cardCategories = function(id) {
angular.forEach($scope.categoryFilter, function(category, index){
if (category.id == id) {
return category.name;
}
});
};

Categories

Resources