shorter code instead of using IF for all the combinations - javascript

I try to add all the combinations using IF but it's a lot to write. If I get the user sign and another user sign for example Aries with Aries I send an output ("high much") but I have 12*12=144 combinations!
Is there any option to do it shorter?
<script>
function hatama() {
signYours= $("input[name='sign1']").val()
signFriend=$("input[name='sign2']").val()
//Aries
if (signYours=="Aries" && signFriend=="Gemini")|| (signYours=="Aries" && signFriend=="Leo")|| (signYours=="Aries" && signFriend=="Saggitarious")|| (signYours=="Aries" && signFriend=="Libra")||(signYours=="Aries" && signFriend=="Aquarius")
$("#output").val("High");
if (signYours=="Aries" && signFriend=="Aries")||(signYours=="Aries" && signFriend=="Virgo") || (signYours=="Aries" && signFriend=="Capricon") ||
$("#output").val("Medium");
if (signYours=="Aries" && signFriend=="Pisces") || (signYours=="Aries" && signFriend=="Cancer")|| (signYours=="Aries" && signFriend=="Scorpio")
$("#output").val("Low");
//Taurus
if (signYours=="Taurus" && signFriend=="Virgo")|| (signYours=="Taurus" && signFriend=="Capricon")||(signYours=="Taurus" && signFriend=="Taurus")||(signYours=="Taurus" && signFriend=="Cancer")||(signYours=="Taurus" && signFriend=="Scorpio")||(signYours=="Taurus" && signFriend=="Pisces")
$("#output").val("High");
if (signYours=="Taurus" && signFriend=="Aquarius")||(signYours=="Taurus" && signFriend=="Taurus")
$("#output").val("Medium");
if (signYours=="Taurus" && signFriend=="Gemini")|| (signYours=="Taurus" && signFriend=="Leo")||(signYours=="Taurus" && signFriend=="Saggitarious")
$("#output").val("Low");
</script>

One option is to use an object whose properties are your signs, whose values are objects with High, Medium and Low properties, whose values are the associated signs:
// Object indexed by your own sign:
const signs = {
Aries: {
High: ['Gemini', 'Leo', 'Saggitarious', 'Libra', 'Aquarius'],
Medium: ['Aries', 'Virgo', 'Capricon'],
Low: ['Pisces', 'Cancer', 'Scorpio']
}
// ...
};
const getAssoc = (signYours, signFriend) => {
const yourObj = signs[signYours];
if (!yourObj) {
return "Your sign is invalid";
}
const foundEntry = Object.entries(yourObj)
.find(([_, arr]) => arr.includes(signFriend));
if (!foundEntry) {
return "Friend's sign is invalid";
}
return foundEntry[0];
};
console.log(getAssoc('Aries', 'Gemini'));
console.log(getAssoc('Aries', 'Scorpio'));
So, for your code:
function hatama() {
const signYours = $("input[name='sign1']").val();
const signFriend = $("input[name='sign2']").val();
$("#output").val(getAssoc(signYours, signFriend));
}
(make sure to always declare variables, else you'll implicitly create properties on the global object, which should be avoided)
If you need to make properties on signs for every possible sign, there may be an even more efficient way, if there is a pattern to the High/Medium/Low associations.

IMHO the problem here is to have a well structured data instead of a sequence of IF statements, checkout my solution by configuration:
const signs = [
{
userSign: "Aries",
associations: [
{
signs: ["Gemini","Leo", "Sagittarius", "Libra", "Acquarius"],
val: "High",
},
// further match
]
},
// furter signs
]
getMatchValue = (user1, user2) => {
let result;
const user1Sign = signs.find(
(sign) => sign.userSign === user1
);
user1Sign.associations.forEach(
(association) => {
if(association.signs.includes(user2)){
result = association.val;
}
}
)
return result;
}
console.log(getMatchValue("Aries", "Gemini"));

Related

How to optimize multiple variable check

const {
service,
customer,
company,
parking,
aircraftType,
aircraft,
endPlan,
startPlan,
heatingPointsMasterCodes,
lavatoryType,
passengersCategory } = formValues;
useEffect(() => {
customer &&
company &&
(parking || service === ReferenceCodesOfServicesEnum.ProvisioningMinibus) &&
aircraftType &&
aircraft &&
endPlan &&
startPlan &&
(heatingPointsMasterCodes ||
lavatoryType ||
passengersCategory ||
formValues[DocumentItemNamesEnum.WaterSystemMaintenance] ||
service === ReferenceCodesOfServicesEnum.AircraftCooling)
? setDisabled(false)
: setDisabled(true);
}, [formValues]);
So my question is, how to optimize or reduce variable check for true value?
First i get variables with destructing from object, then check same variables for true value.
I think i can somehow optimize this, but dont know how
In general if you have a series of if checks you could consider turning it into a switch instead. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
Since you're mostly checking to make sure things are defined, but also have some more nuanced requirements, it may be better break it up slightly:
// Properties required irrespective of environment properties in DocumentItemNamesEnum or ReferenceCodesOfServicesEnum
const requiredProperties = ["customer",
"company", "aircraftType",
"aircraft", "endPlan",
"startPlan"]
const requiredPropertiesDefined = requiredProperties.every(value => !!value)
useEffect(() => {
if (requiredPropertiesDefined &&
(formValues.parking || formValues.service === ReferenceCodesOfServicesEnum.ProvisioningMinibus) &&
&& (formValues.heatingPointsMasterCodes ||
formValues.lavatoryType ||
formValues.passengersCategory ||
formValues[DocumentItemNamesEnum.WaterSystemMaintenance] ||
service === ReferenceCodesOfServicesEnum.AircraftCooling) {
setDisabled(true)
} else {
setDisabled(false
}
}, [formValues])
If you want to check if all values of object are truthy then following code can be used.
Object.values(formValues).every(value => !!value)

Lodash filter based on three or more inputs

Im trying to construct a filter based on three input selectors. The logic is as follows:
If only one of the three inputs has been selected the filter should return all results based on only this selector value and NOT the unselected inputs
If two of the three inputs has been selected the results should be filter based on only these two and NOT the unselected input
If all of the three inputs has been selected the results should be filtered on all three
My code feels verbose and not scalable (see below). Does anyone have ideas on how to optimise this?
This is what i'm working with so far. hasSelectedBuildingtype,hasSelectedInstrument and hasSelectedRegion checks if the inputs have been selected. Accordingly i'm manually checking the state of all the inputs:
<!-- language: lang-js -->
result = _.filter(this.examples, function(entry) {
// Single checks (if only bulding or region or instrument selected)
if (
hasSelectedBuildingtype &&
!hasSelectedRegion &&
!hasSelectedInstrument
) {
if (
entry.buildingtypes.includes(
vm.selectedBuildingtype
)
) {
return true;
}
}
if (
hasSelectedInstrument &&
!hasSelectedRegion &&
!hasSelectedBuildingtype
) {
if (entry.instruments.includes(vm.selectedInstrument)) {
return true;
}
}
if (
hasSelectedRegion &&
!hasSelectedInstrument &&
!hasSelectedBuildingtype
) {
if (entry.regionid === vm.selectedRegion) {
return true;
}
}
// Double checks (if only bulding AND region or instrument AND etc selected)
if (
hasSelectedRegion &&
hasSelectedBuildingtype &&
!hasSelectedInstrument
) {
if (
entry.regionid === vm.selectedRegion &&
entry.buildingtypes.includes(
vm.selectedBuildingtype
)
) {
return true;
}
}
if (
hasSelectedInstrument &&
hasSelectedBuildingtype &&
!hasSelectedRegion
) {
if (
entry.instruments.includes(vm.selectedInstrument) &&
entry.buildingtypes.includes(
vm.selectedBuildingtype
)
) {
return true;
}
}
if (
hasSelectedInstrument &&
hasSelectedRegion &&
!hasSelectedBuildingtype
) {
if (
entry.instruments.includes(vm.selectedInstrument) &&
entry.regionid === vm.selectedRegion
) {
return true;
}
}
// Triple check (if all types selected)
if (
hasSelectedInstrument &&
hasSelectedRegion &&
hasSelectedBuildingtype
) {
if (
entry.instruments.includes(vm.selectedInstrument) &&
entry.regionid === vm.selectedRegion &&
entry.buildingtypes.includes(
vm.selectedBuildingtype
)
) {
return true;
}
}
});
Thanks!
One thing you could do is to use bit alias per boolean flag. You'll need a contract stating the position of each flag in one integer. So suppose the
bit 0: hasSelectedBuildingtype
bit 1: hasSelectedRegion
bit 2: hasSelectedInstrument
...
then your initial assignments would be done this way;
let myInputFlags = hasSelectedBuildingtype << 0 |
hasSelectedRegion << 1 |
hasSelectedInstrument << 2 |
// ... << 3, << 4, etc
and then your comparisons become as simple as comparing to one integer, e.g.
/**
* Checking that hasSelectedBuildingtype === false
* and hasSelectedRegion === false
* and hasSelectedInstrument === true
*/
if (myInputFlags === 4) {
...
You can further create alias for comparison numbers like;
let hasSelected = {
...,
INSTRUMENT_ONLY: 4,
...
};
so that your branches become a lot more expressive. E.g.
if (myInputFlags === hasSelected.INSTRUMENT_ONLY) {
...
This way, if you want to add additional boolean checks in the future, all you'll need to maintain is the values and add an extra bit-wise OR to the formula.

Check for certain value in every key of a Javascript object

I have the following line:
<button className={`actionBoxButton ${props.moves[0].moveName !== "FirstPassMove" && props.moves[0].moveName !== "PassMove" ? "actionBoxButtonGrey" : ''}`}
What it does is to check if the object "moves" has the value "FirstPassMove" for the key moveName. If so, I want it so switch to another style. This is working well.
But what I want to achieve is, to not only check the element 0 of the object, but all the objects and check if there is a moveName "FirstPassMove" in any element of the object.
It is written in React 16.12
Thanks!
You can simplify your jsx by defining a separate function for dynamic className like this:
const check = () => {
let className_ = "";
// if this.props.moves is an array, use of instead of in
for(let i in this.props.moves) {
if((this.props.moves[i].moveName !== "FirstPassMove") && (this.props.moves[i].moveName !== "PassMove")) {
className_ = "actionBoxButtonGrey";
}
}
return className_;
}
<button className={this.check()}>button</button>
props.moves.includes('FirstPassMove')
Assuming your props.moves is an array it will return true or false if this string is inside the props.moves array
If you want to add className in case at least one of your moves has FirstPassMove or PassMove name you should use Array.prototype.some()
const hasPass = moves.some(({ moveName }) => (
moveName === "FirstPassMove" ||
moveName === "PassMove"
));
I am guessing you want to check that all the moves satisfy the constraint, in which case, you can use Array.prototype.every to ensure every move satisfies the constraint. If you only need some moves to satisfy the constraint, you may use Array.prototype.some instead.
// For example
const props = {
moves: [
{ moveName: 'FirstPassMove' },
{ moveName: 'PassMove' },
{ moveName: 'PassMove' },
{ moveName: 'OtherPassMove' },
]
};
function isValidMove({ moveName }) {
return moveName !== "FirstPassMove"
&& moveName !== "PassMove";
}
function getActionBoxButtonClassName(hasColor) {
return `actionBoxButton ${hasColor ? "actionBoxButtonGrey" : ''}`;
}
console.log([
getActionBoxButtonClassName(props.moves.every(isValidMove)),
getActionBoxButtonClassName(props.moves.some(isValidMove))
]);

How can I refactor a long if-else statement checking array length?

I am writing a program that uses checkboxes to filter a list of items.
I have a group of 3 checkboxes: phase, specialty, and type. Once the checkboxes are marked, they are put into an array which is then used to filter out a list if the conditions match.
When making this, I ran into a few issues:
If no boxes were checked, then no list would appear at all.
If a box in all categories were checked, then it would show both conditions rather than when both are true (so if I had phase Base 1 and specialty Race checked, then the list would show all Race matches and all Base 1 matches)
If a box was not checked but another one was, then nothing would appear since it didn't fit both categories.
To fix all three of these issues, I made an if, if/else statement to check the array length of each category. If all arrays (checkboxes) were empty (unchecked), then the original list would appear. If 1 box was checked, but the others weren't, then nothing would break. Etc.. This was made into quite a long statement.
Now, I do plan to add 2-3 more checkbox options, and don't want to complicate things even more. If I keep doing the way that I'm doing now, I may end up with double the current statements I have now.
Currently, the way this list is being filtered is the following, note, the console.logs are to recognize which condition I am using:
if (phases.length === 0 && specialties.length === 0 && type.length === 0) {
const workouts = this.workouts;
this.selectedWorkouts.next(workouts);
} else if (phases.length > 0 && specialties.length > 0 && type.length > 0) {
const workouts = this.workouts.filter(
workout => byPhase(workout) && bySpecialty(workout) && byType(workout)
);
this.selectedWorkouts.next(workouts);
console.log("1 EVERYTHING CHECKED");
} else if (
phases.length > 0 &&
specialties.length > 0 &&
type.length === 0
) {
const workouts = this.workouts.filter(
workout => byPhase(workout) && bySpecialty(workout)
);
this.selectedWorkouts.next(workouts);
console.log("2 PHASE AND SPECIALTY (no type)");
} else if (
phases.length > 0 &&
specialties.length === 0 &&
type.length > 0
) {
const workouts = this.workouts.filter(
workout => byPhase(workout) && byType(workout)
);
this.selectedWorkouts.next(workouts);
console.log("3 PHASE AND TYPE (no specialty)");
} else if (
phases.length > 0 &&
specialties.length === 0 &&
type.length === 0
) {
const workouts = this.workouts.filter(workout => byPhase(workout));
this.selectedWorkouts.next(workouts);
console.log("4 PHASE ONLY (no type or specialty)");
} else if (
phases.length === 0 &&
specialties.length > 0 &&
type.length > 0
) {
const workouts = this.workouts.filter(
workout => bySpecialty(workout) && byType(workout)
);
this.selectedWorkouts.next(workouts);
console.log("5 SPECIALTY AND TYPE (no phase)");
} else if (
phases.length === 0 &&
specialties.length > 0 &&
type.length === 0
) {
const workouts = this.workouts.filter(workout => bySpecialty(workout));
this.selectedWorkouts.next(workouts);
console.log("6 SPECIALTY ONLY (no phase nor type)");
} else if (
phases.length === 0 &&
specialties.length === 0 &&
type.length > 0
) {
const workouts = this.workouts.filter(workout => byType(workout));
this.selectedWorkouts.next(workouts);
console.log("7 TYPE ONLY (no phase nor specialty)");
}
Is there a way to refactor this so I don't continue to add on to the statements, making it an even longer block of code? Or is this not really much of a concern to keep my statement so long?
Here is the stackblitz to my full project. You can find the if/else statements in src/app/features/workouts-page/workoutservice/workout.service.ts. The code is found above should be specific enough for this statement.
Thank you.
Here's how I would do it. This uses a single filter pass:
filterWorkouts(phases: string[], specialties: string[], types: string[]) {
const workouts = this.workouts.filter(workout => {
return (
(phases.length === 0 || phases.indexOf(workout.phase) >= 0) &&
(specialties.length === 0 || specialties.indexOf(workout.specialty) >= 0) &&
(types.length === 0 || types.indexOf(workout.type) >= 0)
);
});
this.selectedWorkouts.next(workouts);
}
You would need to add a one-liner for each additional filter is all. Here's a working implementation on stackblitz for you to play around with.
Since the byPhase and related functions are just functions, you could store them in an array based on the values. Then you could call the functions within the array to pass up to your filter.
function byPhase(workout) { console.log('by phase'); }
function bySpecialty(workout) { console.log('by specialty'); }
function byType(workout) { console.log('by type'); }
// This should filter without specialties
phases = [1,2,3];
specialties = [];
type = [3,4];
const workoutFilters = [
phases.length > 0 ? byPhase : null,
specialties.length > 0 ? bySpecialty : null,
type.length > 0 ? byType: null,
].filter(Boolean);
// Show selected filters
console.log('filters:', workoutFilters);
You can always return from your if to avoid using an else if as long as there isn't something afterwards. In your case, there wasn't so that's one way to shorten things up.
Also, len === 0 can always be replaced with !len as it's a bit more succinct and instead of len > 0 it can just be if (len) if you're comfortable with that, some don't find it as read-able so I'll leave it up to you.
I noticed the same line came after filter op: this.selectedWorkouts.next(workouts); so I moved that to the end and only wrote it once outside of the other blocks to avoid having it run at the end of each block.
Then I noticed that you are basically just trying to use that filter if one of the filters is applied so instead of else if, I used 3 if statements that will apply the filtered param and moved workouts up in scope and gave it its own unique variable so instead of filtering on this.workouts you are filtering on the wo in the fn and updating it each time. This way, you don't have to check for the negative conditions as it won't filter them if the condition doesn't apply and will filter again if a second param applies (gives you the && without writing out every condition).
I think this is a very read-able solution that mostly preserves the code you've written so far.
filterWorkouts(phases: string[], specialties: string[], type: string[]) {
const byPhase = workout => phases.some(phase => workout.phase === phase);
const bySpecialty = workout =>
specialties.some(specialty => workout.specialty === specialty);
const byType = workout => type.some(type => workout.type === type);
let wo = this.workouts;
if (phases.length) {
wo = wo.filter(workout => byPhase(workout));
console.log("CHECKED PHASE");
}
if (specialties.length) {
wo = wo.filter(workout => bySpecialty(workout));
console.log("CHECKED SPECIALTY");
}
if (type.length) {
wo = wo.filter(workout => byType(workout));
console.log("CHECKED TYPE");
}
this.selectedWorkouts.next(wo);
return;
}
}

Multiple filters using lodash in React

I have a function that is filtering a large set of objects based on inputs that looks like so:
filterLocations(filters) {
let filteredLocations = _.filter(
this.state.locations,
location =>
location.beds >= filters.min &&
location.beds <= filters.max &&
location.baths >= filters.bathrooms &&
_.indexOf(filters.buildingTypes, location.buildingType.name) !== -1
);
this.setState({ filteredLocations: filteredLocations });
}
and in another component this is where the filters are set:
let filters = {
min: this.state.min || 0,
max: this.state.max || 99,
bathrooms: this.state.bathrooms || 0,
buildingTypes: this.state.selectedTypes || []
};
The first three work fine because the 'default values' are set regardless, so it makes filtering easy. But I am having trouble with figuring out the last part. If I select a buildingType it's fine and the filtering works as expected, but obviously if I leave it blank, the _.index(...) part tries to sort on nothing, so the resulting array is empty. I was wondering what would be the best way to rework the _.indexOf(...) part so I dont have to do something like:
buildingTypes: this.state.selectedTypes || ['list all options here']
Could you use a ternary to conditionally include the indexOf, and default to true without any buildingTypes so the expression will evaluate to true if all of the others are also true?
filterLocations(filters) {
const filteredLocations = _.filter(
this.state.locations,
location =>
location.beds >= filters.min &&
location.beds <= filters.max &&
location.baths >= filters.bathrooms &&
(filters.buildingTypes.length > 0
? _.indexOf(filters.buildingTypes, location.buildingType.name) !== -1
: true)
);
this.setState({ filteredLocations });
}

Categories

Resources