How to return multiple functions dynamically Javascript - javascript

I apologize if I didn't ask my question in the correct way. Let me explain my problem.
I'm working on a search function in a table, I'm adding the option to search in specific columns.
Right now I'm manually checking if the checkboxes are selected or not, then return the proper search function.
However I want to make it dynamically, I don't want to edit this code after every time I add new columns to the table. That's where I'm struggling.
This is my current code:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
if (vm.searchFilter[0] === true ){
return (contains(row[vm.table.columns[0].value], searchTerm))
}
if (vm.searchFilter[1] === true ){
return (contains(row[vm.table.columns[1].value], searchTerm))
}
if (vm.searchFilter[2] === true ){
return (contains(row[vm.table.columns[2].value], searchTerm))
}
if (vm.searchFilter[3] === true ){
return (contains(row[vm.table.columns[3].value], searchTerm))
}
if (vm.searchFilter[4] === true ){
return (contains(row[vm.table.columns[4].value], searchTerm))
}
if (vm.searchFilter[5] === true ){
return (contains(row[vm.table.columns[5].value], searchTerm))
}
if (vm.searchFilter[6] === true ){
return (contains(row[vm.table.columns[6].value], searchTerm))
}
if (vm.searchFilter[7] === true ){
return (contains(row[vm.table.columns[7].value], searchTerm))
}
});
}
});
This is the way I created it, I tried to use a loop, but I can't return the functions if there is a loop. Also adding return before the for loop wont help either.
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(function (row) {
for (let i=0; i<vm.searchFilter.length; i++){
if (vm.searchFilter[i] === true ){
return (contains(row[vm.table.columns[i].value], searchTerm))
}
}
});
}
});
This is probably a very easy case for the most of you, so I apologize if I'm just being stupid right now. I'm working since 2 hours on this till now...
EDIT:
The function I mean:
const contains = (value, searchTerm) => {
if (!value) return false;
return value.toLowerCase().includes(searchTerm);
}
2nd EDIT:
I just realized after a kind member told me that, that the first version isnt working either the way I want it.
There is the option to have a multiple selection, so if I select the first two checkboxes, then it should search in BOTH of them and not only one.

You can just iterate over the whole thing like this:
vm.$watch('searchTerm', function (searchTerm) {
if (!searchTerm) {
vm.filteredTable = angular.copy(vm.table);
} else {
searchTerm = searchTerm.toLowerCase();
return vm.filteredTable.rows = vm.table.rows.filter(row =>
vm.searchFilter.some((filter, index) => filter && contains(row[vm.table.columns[index].value])))
}
By using Array.prototype.some you essentially ask whether your row matches at least one of the filters.

Related

Unexpected results from a function

I have the following two blocks of code that I am trying to debug.
function getSectionId(target){
let element = target;
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
else {
getSectionId(element.parentElement);
}
};
function coverageLimitHandler(event) {
const target = event.target;
if (target.getAttribute('data-status') !== 'set') {
let itemBlock = addLineItem();
let sectionId = getSectionId(target);
let attribute = '';
console.log(sectionId);
}
}
The event fires and the functions run, but the above gives the unexpected following results
//first-coverage-section (this one is expected.)
//undefined (this is expected to be the same, but is not.)
And I cannot for the life of me figure out why this is happening.
the problem is that your recursive call is not returning anything.
when you do:
getSectionId(element.parentElement);
it will call the function and maybe some day, the if above
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
will return something, but that won't be returned to the previous calls therefore your main call wont have anything to return, so to solve this you need to do this:
function getSectionId(target){
let element = target;
if(element.hasAttribute('id')){
console.log(element.id);
return element.id;
}
else {
// add this return and your function will work correctly.
return getSectionId(element.parentElement);
}
};
basically you have something like this:
function recursiveNotWorking(n) {
if (n === 5) {
return "something"
} else {
// you dont return anything, then something never bubbles up
recursiveNotWorking(n + 1);
}
}
function recursiveWorking(n) {
if (n === 5) {
return "something"
} else {
// we return something
return recursiveWorking(n + 1);
}
}
console.log("NW: ", recursiveNotWorking(1));
console.log("Working: ", recursiveWorking(1));
You need to return the result of the recursive call:
const getSectionId = target => {
if (target.hasAttribute('id') {
return target.id;
}
// You should also check that parentElement exist
// Otherwise you might reach the root node and then parentElement could become null
return getSectionId(target.parentElement);
};
Alos, this can be re-written as well as one liner:
const getSectionId = t => t.id || getSectionId(t.parentElement)
You don't have return in the first function and you don't check on undefined. Also you don't need to use the element variable. It's useless.
Maybe this will work:
function getSectionId(target){
if (typeof target === 'undefined') return null;
if(target.hasAttribute('id')){
console.log(target.id);
return target.id;
}
return getSectionId(target.parentElement);
}

How to Use Conditionals in JS Find Function

I'm wondering how to use an if statement in the JS find function? My goal here is when these values do not match to addClass("filtered-out") to the elements in my cars array.
cars.map(car => active_filters.find(x =>
if (car.attr(x.id) !== x.value)
return car.addClass("filtered-out");
));
I don't know logic you coded is true or not, but you can use simple loop and check condition.
cars.forEach(function(car) {
for (var i = 0; i < active_filters.length; i ++) {
if (car.attr(active_filters[i]['id']) !== active_filters[i]['value']) {
car.addClass("filtered-out");
break;
}
}
});
Or something like this
cars.forEach(function(car) {
var found = active_filters.find(function(el) {
return car.attr(el['id']) === el['value'];
});
if (!found) {
car.addClass("filtered-out");
}
});

React - Filtering returns wrong rows

It's driving me crazy. I've created a list with several entries. I added a filtering function, which seems to work fine. I've checked the number of results returned, but somehow it just showing the result number beginning at the first row.
For explanation:
Let's assume I search for "Zonen" and my filter function returns 4 rows with ID 23, 25, 59 and 60, the rows with ID's 1,2,3 and 4 are displayed. What I'm doing wrong!?
...
render() {
let filteredList = this.state.freights.filter((freight) => {
let search = this.state.search.toLowerCase();
var values = Object.keys(freight).map(function(itm) { return freight[itm]; });
var flag = false;
values.forEach((val) => {
if(val != undefined && typeof val === 'object') {
var objval = Object.keys(val).map(function(objitm) { return val[objitm]; });
objval.forEach((objvalue) => {
if(objvalue != undefined && objvalue.toString().toLowerCase().indexOf(search) > -1) {
flag = true;
return;
}
});
}
else {
if(val != undefined && val.toString().toLowerCase().indexOf(search) > -1) {
flag = true;
return;
}
}
});
if(flag)
return freight;
});
...
<tbody>
{
filteredList.map((freight)=> {
return (
<Freight freight={freight} onClick={this.handleFreightClick.bind(this)} key={freight.id} />
);
})
}
</tbody>
...
UPDATE
freights is loaded and filled via AJAX JSON result. One object of freights looks like this:
I have a textbox where a user can perform a search. This search should return all freight objects which properties contain the search string.
The filter is so complex, because I want to also to search in sub-objects of freight. Maybe there is a more simple way?
"Zones" was just an example for a search string the user can search for.
Now that your intentions are clearer, I suggest this much less complex solution.
First, you can write a recursive utility fn to get all values of all keys in an n-depth object. Like this, for example (I'm using lodash's utility fn isObject there):
const getAllValues = (obj) => {
return Object.keys(obj).reduce(function(a, b) {
const keyValue = obj[b];
if (_.isObject(keyValue)){
return a.concat(getAllValues(keyValue));
} else {
return a.concat(keyValue);
}
}, []);
}
Now that you have an array of all object's values, it makes your filter very simple:
let filteredList = this.state.freights.filter((freightItem) => {
const allItemValues = getAllValues(freightItem);
return allItemValues.includes(this.state.search);
});
That should be it. If something is not working, gimme a shout.
I have found the solution why the "wrong" freight entries are displayed.
I needed to add in freight component the componentWillReceiveProps method:
componentWillReceiveProps(nextProps) {
if(nextProps.freight) {
this.setState({
freight: nextProps.freight
});
}
}
Then everything worked fine.

Javascript promise conversion

I'm having a hard time understanding how promises works. I've seen some examples and some of it makes sense. However, when I try to actually use it I get stuck. I have the following example:
(I'm using q and nodejs. getItem() returns a promise, but my function doesn't wait for it.)
function calculate(id, item) {
var calcId = id ? id : getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Based on the examples, I don't see how to do it without code duplication.
Promises are resolved asynchronously, so you can't treat them as synchronous as in your example. What you want to do is coerce the value is a Q promise if needed, then proceed from there, that way you have deterministic behavior and no code duplication.
I'm making some assumptions here about update and insert returning promises and returning them when needed so that calculate itself returns a promise.
function calculate( id, item ) {
return Q( id || getItem(item) ).then(function( calcId ) {
// Code seems weird to be, but it's based on the original example.
// Might want to review this.
if ( !id && calcId ) {
calcId = calcId.Id;
}
return calcId ?
update( calcId ) :
insert( item );
});
}
Don’t duplicate your id checks:
function calculate(id, item) {
var calcId;
if (id) {
calcId = id;
} else {
calcId = getItem(item).Id;
}
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Now make calcId consistently a promise holding an Id:
function calculate(id, item) {
var p;
if (id) {
p = Promise.resolve({ Id: id });
} else {
p = getItem(item);
}
return p.then(function (calcId) {
if (calcId.Id) {
return update(calcId.Id);
} else {
return insert(item);
}
});
}
Where, in the case of Q, Promise.resolve is Q.resolve, or just Q.
Bonus: as a generator!
function calculate(id, item) {
var calcId = id ? id : yield getItem(item);
if (!id && calcId) { calcId = calcId.Id; }
if (calcId) {
update(calcId);
} else {
insert(item);
}
}
Several points :
Promise-wrapped results need to be handled with a function specified as a parameter of a promise method (eg .then(fn))
Q(x) can be used to ensure that x is Promise-wrapped. The operation is transparent if x is already a promise - it won't be double wrapped
you need safety in case both id and item are empty or missing
you need further safety in case calcId is falsy and item was not provided
id versus calcId.Id can be more elegantly catered for.
function calculate(id, item) {
if(!id && !item) {
throw new Error("'calculate(): parameters empty or missing'");
}
return Q(id ? {Id:id} : getItem(item)).then(function(resultObj) {
var calcId = resultObj.Id;
if(calcId) {
update(calcId);
} else {
if(item) {
insert(item);
} else {
throw new Error("'calculate(): `calcId` is falsy and `item` is empty of missing'");
}
}
return calcId; //in case you need to chain calculate(...).then(...)
//(Alternatively, if update() and insert() return promises, then return them instead as per dherman's answer)
});
}
Call as follows :
calculate(myId, myItem).then(function(calcId) {
//(optional) anything else you wish to do with `calcId`
}).catch(function(e) {
console.error(e);
});

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