Dynamically call function with multiple arguments - javascript

I'm trying to find similar items amongs a dynamic amount of arrays, For example I might have 2 or 3 arrays with data in them, and want to find the which items exist between all of them.
At the minute i've got this "working" but really ugly code which won't scale past 3 items. The GDAX, PLNX etc are all bools which I have available to tell me whether this option is selected.
The intersectionBy is a lodash helper function with further information available here https://lodash.com/docs/4.17.4#intersectionBy
let similarItems = [];
similarItems = GDAX && PLNX && BTRX ? _.intersectionBy(data.BTRX, data.PLNX, data.GDAX, 'pair') : similarItems;
similarItems = GDAX && PLNX && !BTRX ? _.intersectionBy(data.PLNX, data.GDAX, 'pair') : similarItems;
similarItems = GDAX && !PLNX && BTRX ? _.intersectionBy(data.BTRX, data.GDAX, 'pair') : similarItems;
similarItems = !GDAX && PLNX && BTRX ? _.intersectionBy(data.BTRX, data.PLNX, 'pair') : similarItems;

This should do the job
const input = ['GDAX', 'PLNX', 'BTRX']; // here you pass the strings that are given
const result = _.intersectionBy.apply(_, input.map(name => data[name]).concat(['pair']));
The input could also somehow automized, e.g. giving the object of true / false values for each name, so
const inputObject = { GDAX: true, PLNX: false, BTRX: true };
const names = ['GDAX', 'PLNX', 'BTRX'].filter(name => inputObject[name]);
const result = _.intersectionBy.apply(_, names.map(name => data[name]).concat(['pair']));

For readability and easy maintainability, I'd go with explicitly building a selection according to your boolean flags:
let selection = [];
if (GDAX) selection.push(data.GDAX);
if (PLNX) selection.push(data.PLNX);
if (BTRX) selection.push(data.BTRX);
const result = _.intersectionBy(...selection, 'pair');

Related

Nested Object mutates in React

I am in almost desperate need of help. I am a mechanical engineer and I'm doing a type of calculator for my line of work. I have had an issue I've spent weeks on. I can't seem to solve it.
To not bore you with long code I will try to generalise it as much as possible.
I will first present an example code.
Then I will explain the expected behaviour and what is actually happening for me.
Finally I will explain what I have tried so far to solve this issue.
I will add more content at the bottom based on comments to help clarify my question.
CODE EXAMPLE
THE PARENT OBJECT
import {childObject} from "./childObject"
// in my code "childObject" are actually different from each other
const object1 = Object.assign({}, childObject);
const object2 = Object.assign({}, childObject);
const object3 = Object.assign({}, childObject);
const object4 = Object.assign({}, childObject);
const object5 = Object.assign({}, childObject);
const object6 = Object.assign({}, childObject);
const exampleObject = {
name: "foo",
otherInfo: "bar",
nestedObject:{
standardType: [object1, object2, object3],
specialType: [object4, object5, object6]
},
sumfunc(){}
}
THE CHILD OBJECT
export const childObject = {
name: "I'm losing my mind",
value: "" //<-- this will change
otherInfo: "please help me",
sumfunc(){}
}
EXPLAINING
What I am doing is the following:
Searchbar with all types of parentObjects.
Allowing user to select one or multiple of same or different parentObjects.
Storing the copied selection in a redux store.
Displaying the selection, each parentObject as a form. [see picture]
When typing in form the value of the nested object will change
Now... The issue is when I open the searchbar and select the same parentObject, thus copying it, all its values are mutated. As seen in picture above.
WHAT I HAVE TRIED
I have tried to use lodash clone and deepClone on the selected parentObject.
I have tried to use loads clone and deepClone on the selected childObjects.
I have tried, since the object have the same structure, to go through all key value pairs and shallow copy them.
I have tried to not send the parentObject via the searchbar component to the reducer, instead I just send a string and the reducer itself will add the parentObject to the store.
All methods that I've tried have not stopped the mutation. The deepClone method stopped the mutations, but in return the functions in the objects stopped working (maybe I need to bind it somehow?)
MORE CONTENT
The code that updates the value of the nestedObject
const inputsHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
const formCopy = Object.assign({}, formEQ);
const inputFieldName = e.target.name;
// if anything other than a empty, number or decimal inputted, then return
const isNum = e.target.value.match(/^(?:\d{1,8}(?:\.\d{0,8})?)?$/);
if (!isNum) return;
// Update priority list to calculate the last updated input
formCopy.priorityList = formCopy.priorityList.sort((a, b) => {
if (a === inputFieldName) return 1;
if (b === inputFieldName) return -1;
else return 0;
});
// Update selected input field
formCopy.inputs[calcmode] = formCopy.inputs[calcmode].map((input) => {
if (input.name === inputFieldName) {
input.value = e.target.value;
}
return input;
});
// If more than two inputs empty do not calculate
const emptyInputs = formCopy.inputs[calcmode].reduce(
(acc, nV) => (nV.value === "" ? (acc += 1) : acc),
0
);
// Calculate the last edited input field
formCopy.inputs[calcmode] = formCopy.inputs[calcmode].map((input) => {
if (input.name === formCopy.priorityList[0] && emptyInputs <= 1) {
const calculatedValue = formCopy.calculate(
formCopy.priorityList[0],
calcmode
);
input.value = Number(calculatedValue).toFixed(2);
}
return input;
});
// Final set hook, now with calculated value
setformEQ({ ...formCopy });
};
Please good people of StackOverFlow... Help me!
Your code has few problems :
you are filtering based on name property of child object and all of them has the same name. Always provide unique id to the objects so that they can be differentiated in easy manner.
Your filter logic is so wrong :
formCopy.inputs[calcmode] = formCopy.inputs[calcmode].map((input) => {
if (input.name === inputFieldName) {
input.value = e.target.value; // < -- Culprit
}
return input;
});
Never mutate inline, always create a new copy.
This is how your code change function should be (I have removed dynamic key selection for clarity) :
const change = (e, id) => {
const inputFieldName = e.target.name;
// local copy of array
const nestedArr = [...qform.nestedObject.standardType];
// finding item to be updated
const index = nestedArr.findIndex((i) => i.id === id);
console.log({ index, inputFieldName, e, id });
if (index !== -1) {
const item = nestedArr[index];
item.value = e.target.value;
nestedArr[index] = item;
// deep copy till k depth where k is the key to be updated
const newObject = {
...qform,
nestedObject: {
...qform.nestedObject,
standardType: [...nestedArr],
},
};
setQform(newObject);
}}
Check this Example : Demo

Multiple Filter Methods

I'm trying to solve this problem.
I'm creating a filter method, that has to return me a filtered array based on my preference.
The array is containing some sports bet info, like the odd quote, the starting date, the name of the bookmaker, etc, and I'm trying (for the moment) to just filter based on the min and max odds
First of all, into my "Parent" react component, I receive some info from his child, and those info are stored in an object.
The object is like that:
filters = {minOdd: "", maxOdds:"", ecc...}
And the filter method is like:
setFilters = () => {
const odds = this.state.odds
const filters = this.state.filters
const newOdds = odds.filter((odd) => odd.quota > filters.quotaMin)
.filter((odd) => odd.quota < filters.quotaMax)
}
Where "quota" means "odd", quotaMin means "min odd" and "quotaMax" means max odd
Now, if I set the min and the max odd into my child component, the function returns me an array containing all the right odds. But if I set just one of the 2 filters, this function returns me back an empty object.
I'n my opinion, the problem is that if i don't set one of the 2 value, the filter method compares the odd this a value that is like modd.quota < filters.quotaMax, where filters.quotaMax could be = to "".
Soo i have to not allow the filter method to filter value that are = to "".
If someone can give my an advice!
Thanks in advice!
Use fallback values for the undefined filters.
If either quotaMax or quotaMin is not defined, you are (most likely, haven't seen the structure of a bet) comparing a Number against undefined, which always results in false:
1 < undefined; // -> false
1 > undefined; // -> false
As fallback values, you can use negative and positive infinity. To be honest, it doesn't matter which values you use as long as the fallback for quotaMin is guaranteed to be smaller than the lowest quota and the fallback for quotaMax is guaranteed to be higher than the highest quota.
const newOdds = odds
.filter(odd => odd.quota > (filters.quotaMin || -Infinity))
.filter(odd => odd.quota < (filters.quotaMax || Infinity));
Side note:
You can make your code run faster by merging both predicates into one with AND && (saves one iteraton/filtering).
const newOdds = odds
.filter(odd => odd.quota > (filters.quotaMin || -Infinity) &&
odd.quota < (filters.quotaMax || Infinity));
I guess you just need to handle that case then where quotaMax is undefined/"":
const newOdds = odds.filter((odd) => odd.quota > filters.quotaMin)
.filter((odd) => {
if (filters.quotaMax) {
return odd.quota < filters.quotaMax
} else {
// you decide what should happen in this case..
// return true/false
})

Inherited truth check in condition comparison

I'm looking for a clean way to get true or false based on an order of permissions based on the following rules:
Starts with Company Permissions as a default
Then to Team Permissions if permission defined
Finally to User Permissions if permission is
This would need to also handle undefined. So basically wanting to see if there's some "clean" way to do this without having to conditionally check each value and moving on.
In this example, the result should be false since there are no User Permissions defined and the Team Permissions has false.
const UserPermissions = {}
const TeamPermissions = {
PERMISSION_ONE: false
}
const CompanyPermissions = {
PERMISSION_ONE: true
}
const hasPermissions = UserPermissions.PERMISSION_ONE || TeamPermissions.PERMISSION_ONE || CompanyPermissions.PERMISSION_ONE
console.log(hasPermissions)
Thanks!
From my understanding, the rules are:
ignore undefined
return true of false, whatever comes first
This little function should handle that, not sure how you want to deal with empty (or all undefined) arguments.
let x;
let t = true;
let f = false;
let firstBool = (...a) => Boolean(a.filter(x => typeof x !== 'undefined').shift());
console.log(firstBool(x,t,f));
console.log(firstBool(t,x,f));
console.log(firstBool(x,f,t));
console.log(firstBool());
In your example, that would be
const hasPermissions = firstBool(
UserPermissions.PERMISSION_ONE,
TeamPermissions.PERMISSION_ONE,
CompanyPermissions.PERMISSION_ONE
]
If you are looking for the same property name in multiple objects, you might be able to slightly alter the technique in georg's answer:
const firstBoolProp = (...objs) => (prop) =>
objs.reduce((a, o) => Boolean(a) === a ? a : o[prop], undefined)
const UserPermissions = {}
const TeamPermissions = {PERMISSION_ONE: false}
const CompanyPermissions = {PERMISSION_ONE: true}
console .log (
firstBoolProp
(UserPermissions, TeamPermissions, CompanyPermissions)
('PERMISSION_ONE')
)
You can then use a single function to multiple permissions against that same set of permission objects:
const getPermissions = firstBoolProp(UserPermissions, TeamPermissions, CompanyPermissions)
const perms = {
p1: getPermissions('PERMISSION_ONE'),
p2: getPermissions('PERMISSION_TWO'),
}
//=> {p1: false, p2: undefined}
And if you want to use an array rather than individual parameters, you can simply replace (...obj) => with (obj) =>
One way to do this is to store the permissions in an array and reduce it to a boolean:
/**
*
* #param {{PERMISSION_ONE:boolean}[]} permissions
*/
function anyoneHasPermission(permissions, name = "PERMISSION_ONE") {
for (const perm of permissions) {
if (perm[name]) {
return true;
}
}
}
console.log(anyoneHasPermission([UserPermissions, TeamPermissions, CompanyPermissions]))
You need to be more specific in what you want to accomplish. Choosing the right data structure is usually more important than choosing an algorithm. Indeed, sometimes the right data structure makes the algorithm disappear completely.
Throw the permissions into an array in the order that you want them validated.
Iterate the array and if any of your conditions is not met then return false and break.
This will stop you running down the entire chain if say your top level permission is not set (because nothing beyond that matters anymore and no use validating it)
const UserPermissions = {PERMISSION_ONE: true}
const TeamPermissions = {}
const CompanyPermissions = {PERMISSION_ONE: true}
const permCheck = HasPerms([CompanyPermissions, TeamPermissions, UserPermissions]);
alert(permCheck);
function HasPerms(permArray){
for (var i = 0; i < permArray.length; i++) {
if (!permArray[i] || !permArray[i].PERMISSION_ONE) {return false;}
}
return true;
}
You can expand the function to dynamically take in the permission you would like to check but example shown hardcoded for simplicity sake.

ReactJS / ES6: Searching Japanese text using includes?

So, I'm writing a client-side search and I need to look through strings of Japanese characters. I'm wondering how to do this properly?... i.e. Do I change the format of the text into utf-8 something and then search the utf-8?
Example:
All my data has japaneseData.title : "フェリーチェ三田"
When I type in my search.value as : "フェ" using japaneseData.title.includes(search.value) I don't get a match...
How do I do this correctly?
Okay, after further inspection, the comments were correct and includes was finding the substring. This is all happening inside of a filter() and I'm trying to return the objects that match...
After changing my code to:
let filteredArrayofObjects = Lists.houseLists.filter(house => house.building_name.includes(query.search));
I was getting back some but not all. Problem cases:
"アーバイルスパシエ芝浦BAY-SIDE".includes("エ芝浦"); // this evaluates to true, but does not get included in my filtered array...
Okay, further digging, it seems the issue is I need to wait for the filter process before returning the results... haven't yet found a solution to that just yet.
async filter(arr, callback) {
return (await Promise.all(
arr.map(async item => {
return (await callback(item)) ? item : undefined;
})
)).filter(i => i !== undefined);
}
handleFilterLists = async (query = {}) => {
const { Lists } = this.props;
let searchResults = await this.filter(Lists.houseLists, async house => {
return house.building_name.includes(query.search);
// the final evaluation to look similar to this:
// var newArray = homes.filter(function (el) {
// return el.price <= 1000 &&
// el.sqft >= 500 &&
// el.num_of_beds >=2 &&
// el.num_of_baths >= 2.5;
// });
});
this.setState({ searchResults });
}
Okay, so, I'm trying to set state.searchResults after the filter method has checked for matching objects in the array Lists.houseLists...
includes returns true or false if the substring is detected or not. If you want the index of where the first detected substring begins, use indexOf.
I used your sample source and search text with includes and it returns true.
Edit:
I used your updated data and this still works. https://codepen.io/anon/pen/RMWpwe
const sourceText = 'アーバイルスパシエ芝浦BAY-SIDE';
const searchText = 'エ芝浦';
const lists = [
'スパシエ',
'芝浦BAY-SIDE',
'エ芝浦',
'パシエ芝浦BAY'
];
console.log(lists.filter(item => item.includes(searchText)));
// ["エ芝浦", "パシエ芝浦BAY"]

React.js – filter multiple indexOf

Im just looking for a bit of advice regarding React.js filtering. I am currently filtering ‘peopleList’ by ‘employeeName’ and this is working fine, im getting back exactly what I expect.
But I wanted to also filter by the ‘employeeID’ at the same time i.e. check if ‘employeeName’ or ‘employeeID’ contain an indexOf.. Is this possible or would I need to set up two filters for 'employeeName’ and 'employeeID’?
let people= this.state.peopleList.filter(
(person) => {
return person.record.employeeName.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
// return person.record.employeeID.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
}
);
If your condition is either one OR the other, you can use the || operator
const { search, peopleList } = this.state
const searchStr = search.toLowerCase()
const people = peopleList.filter((person) =>
person.record.employeeName.toLowerCase().indexOf(searchStr) !== -1 ||
person.record.employeeId.indexOf(searchStr) !== -1
)

Categories