Multiple If Else Statements, only first is run - javascript

I have code that requires multiple If Else statements but I'm not sure how to format it so that each runs:
let example = first;
let example2 = second;
let example3 = third;
if (example === something) {
return null;
} else {
return something;
}
if (example2 === somethingElse) {
return null;
} else {
return somethingElse;
}
if (example3 === somethingMore) {
return null;
} else {
return somethingMore;
}
But this doesn't work because of the multiple else statements, I was wondering if there was a way to do this? I also tried to put the data into an array or objects to iterate through but that won't work either.
Please help! :)

return will immediate return from first if, so store all result in object or array and return it as below
let example = 'first';
let example2 = 'second';
let example3 = 'third';
var return_data = {};
if (example === 'something') {
return_data.example = null;
} else {
return_data.example = something;
}
if (example2 === 'somethingElse') {
return_data.example2 = null;
} else {
return_data.example2 = 'somethingElse';
}
if (example3 === 'somethingMore') {
return_data.example3 = null;
} else {
return_data.example3 = 'somethingMore';
}
return return_data;

You have to remove the return in the if / else blocks - using return will immediately exit the function wherever it's encountered. The way your code is now, you are basically short-circuiting the function (which is not what you're trying to do):
It would probably make more sense to restructure your code to use a variable like this:
//Add a variable to keep store your desired output if you want to flow thru all if/else blocks
function getVal(example) {
let val;
if (example === 'something1') {
val = 'a'
} else {
val = 'b';
}
return val;
}
console.log(getVal('something1'));
console.log(getVal('lorem'));

I'm not completely clear on what you are asking, but I think you want to be using "else if" statements: https://ncoughlin.com/javascript-notes-conditional-statements-loops/#If_Else_If_Else
let example = first;
let example2 = second;
let example3 = third;
if (example === something) {
return a;
} else if (example2 === somethingElse){
return b;
} else if (example3 === anotherThing){
return c;
} else {
return null;
}

You can do something like this :
myArray = [];
let example = first;
let example2 = second;
let example3 = third;
if (example === something) {
myArray.push(null);
} else {
myArray.(something);
}
if (example2 === somethingElse) {
myArray.push(null);
} else {
myArray.(somethingElse);
}
if (example3 === somethingMore) {
myArray.push(null);
} else {
myArray.(somethingMore);
}
return myArray;
Like Tom O. said return will immediatly exit your function. You can use something other than an array but remember return is executed only once.

Regardless of your approach, it seems like you want to build a "collection" of some sort (array, object, set, map, etc) then return it at the end.
But, the way you code it depends on the reason your function exists. Let's look at an example...
if (first === undefined) {
return null
} else {
return first
}
...This logic exists solely to ensure a "default" value is used for first - something like the null object pattern. For this use case, I might propose nullish coalescing to keep it simple (or something that could be easily replaced with it in the future):
first ?? null
// or, if you don't use babel/some kind of transpiler, you could want:
first !== undefined && first !== null ? first : null
// and since our default is null anyway, we can shorten this to:
first !== undefined ? first : null
Looking solely at your example, it seems like you could simply want to get default values like this for multiple variables. For that use case, you (or someone else coming across this question) might want a function similar to one in the code snippets below. Using objects and/or arrays for this can be handy because they can also be easily broken back out into multiple variables, if you wanted.
First, example functions using arrays:
// If you want default values for items in an array (static, all same default value)
const buildArrayWithDefault = (vals, defaultVal = null) => vals.map(
v => v !== undefined ? v : defaultVal // could be v ?? defaultVal
)
// If you want default values for items in an array (static, but defaults could all be different)
const buildArrayWithDefaults = (vals, defaultVals) => vals.map(
(v, idx) => v !== undefined ? v : defaultVals[idx] // could be v ?? defaultVals[idx]
)
// If you want default values for items in an array (dynamic via callback)
const buildArrayWithDefaults2 = (vals, getDefaultValue) => vals.map(
(v, idx) => v !== undefined ? v : getDefaultValue(v, idx)
)
// All of these return [ 1, 5, 3 ]
console.log(
buildArrayWithDefault([1, undefined, 3], 5),
buildArrayWithDefaults([1, undefined, 3], [ 4, 5, 6 ]),
buildArrayWithDefaults2([1, undefined, 3], (v, idx) => idx + 4)
)
Next, examples using objects:
// Hard-coded default values for an object (ternary)
const buildObject = (first, second, third) => ({
first: first !== undefined ? first : null, // or first ?? null
second: second !== undefined ? second : null,
third: third !== undefined ? third : null,
})
// Hard-coded default values for an object (default parameters)
const buildObject2 = (
first = null,
second = null,
third = null
) => (
{ first, second, third }
)
// ...or you can just use Object.assign()
const assignDefaults = (obj) => Object.assign(
{ first: null, second: null, third: null }, // defaults
obj
)
// Finally, allowing the function user to define their own defaults
// (At this point, you may just want to use Object.assign() directly)
const assignDefaults2 = (...args) => Object.assign({}, ...args.reverse())
// All of these should return { first: 1, second: null, third: null }
console.log(
buildObject(1),
buildObject2(1),
assignDefaults({ first: 1 }),
assignDefaults2({ first: 1 }, { first: null, second: null, third: null })
)

Related

How to deal with a `Variable 'xxx' is used before being assigned.`

I have a code block like below where I need to find something inside a loop, and also return a second variable. So I can't use a simple Array.find or Array.some good ole' for...of is my friend. map/filter don't allow a break and find can only return actual elements from the array, not a related calculation.
But the below within typescript is giving me an unavoidable error.
I'm wondering if either there's a more idiomatic way to do this, or a better structure / place to declare the variable?
Variable 'found' is used before being assigned.
let found: ParseResult
// breaks when first item found but we capture a different value
for (const rule of ParserRules) {
// const rex = new RegExp(route.match)
const parsed = rule.rex.exec(input)
if (parsed) {
found = { parsed, rule }
break
}
}
// #ts-ignore
return found // FIXME used before defined?
Here are the various JS iterator methods I tried...
const ar = [1, 2, 3, 4]
const log = console.log
const finder = (list) => {
console.log('map', list.map(it => it === 3))
console.log('find', list.find(it => it === 3))
console.log('some', list.some(it => it === 3))
console.log('filter', list.filter(it => it === 3))
console.log('find', list.find(it => {
if (it === 3) return it * 2 // value coerced to T|F
}))
console.log('filter', list.filter(it => {
if (it === 3) return it * 2 // value coerced to T|F
}))
const arr = list.forEach((k) => {
if (k === 3) return ('here')
})
log('arr', arr)
let found
for (const elem of list) {
log('elem of', elem)
if (elem === 2) {
found = elem
break
}
}
log('found', found)
}
finder(ar)
The summary of your problem is that a function returning a value in a variable when, at times, the logic doesn't get a chance to assign any value to it.
You can either initialize the variable with a default value or, at the point of returning, check if it really has a value.
let found: ParseResult= {}
OR
return found || false //Or an empty object etc
This can be done in many ways but what might suit your case would be
ar
.map((rule) => {
const parsed = rule.rex.exec(input)
if (parsed) {
return { parsed, rule }
}
})
.find((x) => !!x)
Yes you are looping it once more but this is more readable. Also it would not be that costly.
If your processing is heavy, you can try this approach as well but this will be a custom implementation and will not come out of the box:
function getParsedValue(ar, input) {
let parsed;
const rule = ar
.find((rule) => {
parsed = rule.rex.exec(input);
return !!parsed;
})
return !!rule ? { rule, parsed } : null
}

Validating if object data is "true" and retrieving relevant data if so

I have a problem to solve using some data from an object. The data could take a few forms and may or may not exist in the first place. For example
things : {
oranges: true,
apples : false
}
but it could equally be:
things : {
oranges: false,
apples : false
}
or maybe things doesn't even exist
I need to:
1) Determine that things exists
2) Determine that things contains further keys
These two statements need to be verified in one callable function e.g thingsHasData()
3) If things does have data, is any of the data set to true?
This also needs to be a callable function e.g fruitsIsTrue()
4) Return the key for one of the true values
trueFruit() - this should only return one key, but it doesn't matter which (it shouldn't ever have two true values as per business rules but it's more of a fallback to just return one if for some reason it does)
So I've been able to get the key of a true key-value pair using the following:
var thingsList = {
things : {
oranges: false,
apples : true
}
}
var trueFruit = Object.keys(thingsList).filter(function(key) {
return thingsList[key];
});
return thingsList[0];
This correctly returns apples and only apples so it works for point 4 but not the others, and I feel like there is a better way to do this not having to rely on repeating the same .filter in a few different functions. Ideas?
You could take functions and for a true value, use Array#find.
function thingsHasData(object) {
return 'things' in object;
}
function fruitsIsTrue(object) {
return 'things' in object && Object.values(object.things).some(Boolean);
}
function trueFruit(object) {
return 'things' in object && Object.keys(object.things).find(k => object.things[k]);
}
var a = {},
b = { things: {} },
c = { things: { oranges: true, apples : false } },
d = { things: { oranges: false, apples : false } };
[a, b, c, d].forEach(o => console.log(
thingsHasData(o),
fruitsIsTrue(o),
trueFruit(o)
));
To check if the Object things exist, you can use the following code:
if (typeof things != "undefined") {
// It exists!
}
To check if an object has any children, check Object.keys(things).length > 0.
So the check for 1) and 2) would look like:
let things = {
oranges: true,
apples: false
}
if (typeof things != "undefined") {
// It exists!
if (Object.keys(things).length > 0) {
// It has children!
}
}
var thingsList = {
things : {
oranges: false,
apples : true
},
things2 : {
oranges: true,
apples : true
}
};
function validateThings(things) {
// (1) checks for a falsy value of things
if (!things) {
return false;
}
var keys = Object.keys(things);
// (2) checks if things has keys
if (!keys.length) {
return false;
}
// (3) then it checks for every single keys value if it is truthy
for (var i = 0, len = keys.length; i < len; i++ ) {
if (things[keys[i]]) {
// (4) return this value — all tests passed
return things[keys[i]];
}
}
return false;
}
console.log(validateThings(thingsList.notInList));
console.log(validateThings(thingsList.things));
console.log(validateThings(thingsList.things2));
const thingsHasData = arg => (arg.things && Object.keys(arg.things).length>0) ? true : false;
const trueFruit = arg => {
if (!arg.things) return;
let fruitIndex = null;
let fruitValues = Object.values(arg.things);
fruitValues.forEach((value, index) => {
if (value) fruitIndex = Object.keys(arg.things)[index];
});
return fruitIndex;
}

javascript - checking for a property on a nested object with 'property' in object fails

I have the following object called this.props:
{
children: null,
location: {
action: 'POP',
query: {
seconds: '300'
}
}
}
After looking at this question I wrote the following code:
let seconds = 0;
console.log(this.props);
if('seconds' in this.props) {
seconds = parseInt(this.props.location.query.seconds, 10);
}
console.log(seconds);
In the console the object is logged as above, but seconds is logged as 0.
I do not understand what is wrong with my if check, it seems that the condition is failing even though the nested object has the correct property (the issue is that when there is no query object, the whole this.props object is empty - so I need to check for nested properties).
Seconds is not in this.props; it's on query. The in operator is not recursive, just first level:
if('seconds' in this.props.location.query) {
seconds = parseInt(this.props.location.query.seconds, 10);
}
console.log(seconds); // 300
If you want to do it recursively, create your own function:
let isIn = function (obj, key) {
return isObj(obj) ? (key in obj) || Object.values(obj).filter(f => isIn(f, key)).length > 0 : false;
}
Where isObj is a function that verifies if the parameter is an object. You can use lodash or equiv., or make something like:
let isObj = t => (t !== null) && (typeof t === 'object');

Shortest way to set value to variable in Knockout

In Knockout I have observable variable location. It is of type LocationEdit. This viewModel has observable and not fields.
I have collection of field names : fields. For each field I want to reset values for location
fields.forEach(field => {
if (this.uniqueField(locs, field)) {
if (ko.isObservable(this.location()[field])) {
this.location()[field](locs[0][field]);
} else {
this.location()[field] = locs[0][field];
}
}
});
To make this code more simpler (remove if-clauses), Can I somehow set value to this.location()[field] in one line?
You could use the conditional operator (... ? ... : ... ;) although it doesn't change much:
fields.forEach(field => {
if (this.uniqueField(locs, field)) {
ko.isObservable(this.location()[field]) ? this.location()[field](locs[0][field]) : this.location()[field] = locs[0][field];
}
});
Or you could write a function:
function upd(arr, index, val) {
ko.isObservable(arr[index]) ? arr[index](val) : arr[index] = val;
}
Usage:
fields.forEach(field => {
if (this.uniqueField(locs, field)) {
upd(this.location(), field, locs[0][field]);
}
});
See demo.
You could even add this function to ko:
if(typeof ko.updatePotentialObservable == 'undefined')
ko.updatePotentialObservable = function (arr[index], val) {
ko.isObservable(obj) ? arr[index](val) : arr[index]= val;
}
Usage:
fields.forEach(field => {
if (this.uniqueField(locs, field)) {
ko.updatePotentialObservable(this.location(), field, locs[0][field]);
}
});
See other demo
To be honest, I think Gôtô's answers are definitely your best options. Basically, you'd want to create a utility function similar to ko.unwrap but setting a value.
But since you said "also want to find another solution", here's a different utility function. I think the most confusing part of your code is the returning calls to locs[0][field] and this.location()[field]. I'd want something with this signature:
reset(source, target, keys);
So, in your code, you could do:
reset(
this.location(),
locs[0],
fields.filter(f => this.uniqueField(locs, f))
);
Now, writing this method, I ended up with this:
const mergePropsObs = (function() {
// Return a method for setting a specific property in object
const getSetter = obj => prop => ko.isObservable(obj[prop])
? obj[prop]
: val => obj[prop] = val;
// Return unique keys for two objects
// (I went with a quick oneliner; there are many ways to do this)
const allKeys = (obj1, obj2) =>
Object.keys(Object.assign({}, obj1, obj2));
return (base, ext, onlyProps) => {
const props = onlyProps || allKeys(base, ext);
const values = props.map(p => ko.unwrap(ext[p]));
props
.map(getSetter(base))
.forEach((setter, i) => setter(values[i]));
};
}());
var base = { a: 1, b: ko.observable(2), c: 5 };
mergePropsObs(
base,
{ a: 2, b: 3 },
["a", "b"]);
console.log(base.a);
console.log(base.b());
console.log(base.c);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
This utility method can be used with the signature mentioned above. It also has a fallback for when you don't provide an array of field names.

javascript equivalent to python's dictionary.get

I'm trying to validate a JSON object with node.js. Basically, if condition A is present then I want to make sure that a particular value is in an array which may not be present. I do this in python using dictionary.get because that will return a default value if I look up something that isn't present. This is what it looks like in python
if output.get('conditionA') and not 'conditionB' in output.get('deeply', {}).get('nested', {}).get('array', []):
print "There is an error somewhere you need to be fixing."
I'd like to find a similar technique for javascript. I tried using defaults in underscore to create the keys if they aren't there but I don't think I did it right or I'm not using it the way it was intended.
var temp = _.defaults(output, {'deeply': {'nested': {'array': []}}});
if (temp.hasOwnProperty('conditionA') && temp.deeply.nested.array.indexOf('conditionB') == -1) {
console.log("There is an error somewhere you need to be fixing.");
}
It seems like if it runs into an output where one of the nested objects is missing it doesn't replace it with a default value and instead blows with a TypeError: Cannot read property 'variety' of undefined where 'variety' is the name of the array I'm looking at.
Or better yet, here's a quick wrapper that imitates the functionality of the python dictionary.
http://jsfiddle.net/xg6xb87m/4/
function pydict (item) {
if(!(this instanceof pydict)) {
return new pydict(item);
}
var self = this;
self._item = item;
self.get = function(name, def) {
var val = self._item[name];
return new pydict(val === undefined || val === null ? def : val);
};
self.value = function() {
return self._item;
};
return self;
};
// now use it by wrapping your js object
var output = {deeply: { nested: { array: [] } } };
var array = pydict(output).get('deeply', {}).get('nested', {}).get('array', []).value();
Edit
Also, here's a quick and dirty way to do the nested / multiple conditionals:
var output = {deeply: {nested: {array: ['conditionB']}}};
var val = output["deeply"]
if(val && (val = val["nested"]) && (val = val["array"]) && (val.indexOf("conditionB") >= 0)) {
...
}
Edit 2 updated the code based on Bergi's observations.
The standard technique for this in JS is (since your expected objects are all truthy) to use the || operator for default values:
if (output.conditionA && (((output.deeply || {}).nested || {}).array || []).indexOf('conditionB') == -1) {
console.log("There is an error somewhere you need to be fixing.")
}
The problem with your use of _.defaults is that it's not recursive - it doesn't work on deeply nested objects.
If you'd like something that's a little easier to use and understand, try something like this. Season to taste.
function getStructValue( object, propertyExpression, defaultValue ) {
var temp = object;
var propertyList = propertyExpression.split(".");
var isMatch = false;
for( var i=0; i<propertyList.length; ++i ) {
var value = temp[ propertyList[i] ];
if( value ) {
temp = value;
isMatch = true;
}
else {
isMatch = false;
}
}
if( isMatch ) {
return temp;
}
else {
return defaultValue;
}
}
Here's some tests:
var testData = {
apples : {
red: 3,
green: 9,
blue: {
error: "there are no blue apples"
}
}
};
console.log( getStructValue( testData, "apples.red", "No results" ) );
console.log( getStructValue( testData, "apples.blue.error", "No results" ) );
console.log( getStructValue( testData, "apples.blue.error.fail", "No results" ) );
console.log( getStructValue( testData, "apples.blue.moon", "No results" ) );
console.log( getStructValue( testData, "orange.you.glad", "No results" ) );
And the output from the tests:
$ node getStructValue.js
3
there are no blue apples
No results
No results
No results
$
You can check that a key exists easily in javascript by accessing it.
if (output["conditionA"]) {
if(output["deeply"]) {
if(output["deeply"]["nested"]) {
if(output["deeply"]["nested"]["array"]) {
if(output["deeply"]["nested"]["array"].indexOf("conditionB") !== -1) {
return;
}
}
}
}
}
console.error("There is an error somewhere you need to be fixing.");
return;

Categories

Resources