javascript reusable function - javascript

people, I have a question. I have two javascript functions that do the same thing. Note that it only changes the "let selected" variable. I don't think it's the best way to use functions in js, how can I reuse them?
First function:
onChange(id) {
let selected = this.state.selectedDevice
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
Second function:
onChangeSec(id) {
let selected = this.state.selectedSection
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
thanks !!!

You could just pass key as another function parameter
onChange(id, key) {
let selected = this.state[key]
let find = selected.indexOf(id)
if(find > -1) {
selected.splice(find, 1)
} else {
selected.push(id)
}
this.setState({ selected })
}
and in your case key would be 'selectedDevice' or 'selectedSection' passed as a string.

It seems like this.state.selectedDevice and this.state.selectedSection are both arrays, and since you're using index-based retrieval from the array, there's no way from within the function to determine which should be used. Instead you should pass that information into the function. For example:
onChange(id, data) {
const position = data.indexOf(id)
this.setState({
selected: position > -1
? [...data.slice(0, position), ...data.slice(position + 1)]
: [...data, 1]
})
}
Note: this also works around your mutation of an object in state, which will cause bugs.
You might also want to consider using a Set instead:
constructor() {
this.state = {
selected: new Set()
}
}
onChange(value) {
const newSelected = new Set(this.state.selected)
if (newSelected.has(value)) {
newSelected.delete(value)
} else {
newSelected.add(value)
}
this.setState({ selected: newSelected })
}
This is safer than an array where you can have multiple entries of the same value.

Related

Conditional filter function

I have a little problem with my filter function. I have a data structure as shown in the image below.
As you can see, I have an array of objects named bridals and it keeps another array of objects named plans. So inside that I am trying to filter people.
Here is the filter function.
export function peopleFilter(bridals, people){
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal.plans.filter((item) => {
if (item.people === people) {
return bridals;
}
})
})
return bridals;
}
}
The function peopleFilter() should filter plans with selected value only and return with bridals, but it returns nothing. No error is shown as well.
So I tried something like below.
bridals.forEach(bridal => {
bridal.plans.slice().reverse().forEach((item, index, object) => {
if (item.people !== people) {
bridal.plans.splice(object.length - 1 - index, 1)
}
})
})
return bridals;
This above code is doing what I want. But there is one problem. So in the end when I select no value, it should display all plans. But it doesn't because I already removed the plans using splice() every time I select some value.
So I am stuck about this. How can I fix it?
In this case it might be easier to iterate over your bridal array with a map. Within the map, filter plans for each bridal, then return a new obj, which can be accomplished with spread syntax.
const filterFunc = (bridals, people) => {
return bridals.map(bridal => {
const filteredPlans = bridal.plans.filter(plan => plan.people === people);
return { ...bridal, plans: filteredPlans };
})
}
Let's say you want to know the people count for all bridals and plans:
var total = 0;
bridals.forEach(function(b){
b.plans.forEach(function(o){
total += o.people;
});
});
Of course, I can't really tell what you're trying to do. I could write an entire API for this.
You need to return a boolean value inside the filter() instead of return bridals;. Additionally, this returned array needs to be reassigned to bridal array as well.
export function peopleFilter(bridals, people) {
if (people.length === 0) {
return bridals;
} else {
bridals.forEach(bridal => {
bridal = bridal.plans.filter((item) => {
return item.people === people;
})
})
return bridals;
}
}

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

How to pass an instance of an array to a function

I have a checkbox group that I want to get all checked items. I am trying to pass an Array to a function so I can get all checked items but it's not working.
checkedCategory: Array<number>;
contains(checkedArr: Array<number>, id: number): boolean {
if (checkedArr instanceof Array) {
return checkedArr.indexOf(id) > -1;
} else if (!!checkedArr) {
return checkedArr === id;
}
return false;
}
private add(checkedArr: Array<number>, id: number) {
if (!this.contains(checkedArr, id)) {
console.log('add: ' + checkedArr);
if (checkedArr instanceof Array) {
checkedArr.push(id);
} else {
checkedArr = [id];
}
}
}
private remove(checkedArr: Array<number>, id: number) {
const index = checkedArr.indexOf(id);
if (!checkedArr || index < 0) {
return;
}
checkedArr.splice(index, 1);
}
toggleCategory(id: number) {
if (this.contains(this.checkedCategory, id)) {
this.remove(this.checkedCategory, id);
} else {
this.add(this.checkedCategory, id);
}
}
I have a (click) event in my checkbox that will call togglecategory
(click)="toggleCategory(category.id)"
Then, when I try to console.log the 'checkedCategory' it's undefined.
I have 3 checkboxes group and I want to reuse the 'contains/add/remove' function that's why I want to pass an array.
Thank you
When you call toggleCategory(20) see what happens, in your case you will see that your function will print add: undefined. so the first thing you must debug is your add function. I think the issue is that your array is not defined. Try to initalize your empty array like this let checkedCategory: Array<number> = Array();
But either way, You need to debug your add function. Good Luck :)
If you have any questions about why this is the solution, let me know, I dont mind sharing the Theory aspect to why this occurs if you are interested.

create react state name with variable reference?

i want to create state like this:
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ `${name}`: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
I am mapping on my array columns, so each item on the array, i want to set state on them as key, is there a possibe way?
Is there a possible way?
Yes, but the way you are trying is not correct, instead of calling setState inside loop, first prepare an object with all the key-value, then pass that object to setState.
Like this:
componentWillReceiveProps(nextProps) {
let obj = {};
nextProps.columns.forEach((c, i) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
obj[name] = this.props.activeHeaders.indexOf(c) > -1;
});
this.setState(obj);
}
Didn't get the meaning of this line:
const name = nextProps.columns[nextProps.columns.indexOf(c)];
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ [name]: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
This should do the job

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.

Categories

Resources