JavaScript: Quick return if function returns null - javascript

Is there a way to shorten this fragment of code?
const result = getResult();
if (!result) {
return;
}
// Work with result
I keep having lots of these in my code and would love to do something like:
const result = getResult() || return;
// Work with result
EDIT:
I only want convertable inputs to be persisted.
const parseInput = (input: string): void => {
const convertedInput = convert(input);
if (!convertedInput) {
return;
}
persist(convertedInput);
}
I know I could call the converter twice. But I want to avoid that:
const parseInput = (input: string): void => {
if (!convert(input)) {
return;
}
persist(convert(input));
}

Your code is as good as it gets, however, if you want to experiment a bit with the functional style, you can wrap the value into a "monad", which would invoke attached functions only if the value is non-zero. Here's a toy implementation:
function maybe(x) {
return {
value: x,
apply(fn) {
if (this.value)
this.value = fn(this.value)
return this;
}
}
}
With this maybe, your example would look like:
const parseInput = input => maybe(convert(input)).apply(persist)
See the Oliver's answer for a more serious approach.

You can do this
const result = "default value" || getResult();
If getResult is null or not defined then you'll get result as "default value". If that's what you want
function getResult() {
return null;
}
const result = "okay" || getResult();
console.log(result)
And when getResult is not defined you get
const result = "okay" || getResult();
console.log(result)
Basically, the syntax is
null || undefined || null || 0 || "okay" || "defined" // "okay"
It goes from left to right and picks the most relevant value

I don't really know if this answer will give you something that you'll be happy with, but it seems to me to present a potential solution to the problem of handling unknown results.
Maybes are structures which have this kind of checking built-in. the .map() below will not be called if there is no value in the Maybe, so the code which consumes it does not need to check whether a value is present.
This does mean that you have to change the way in which you handle these values however, and, unless you want to write your own, it means using a library. As such this is hardly an ideal solution, but I hope it gives an option at least.
const { None, Some } = Monet;
const getResult = () => Math.random() > 0.5
? None()
: Some(1);
const test = getResult()
.map(x => x + 2);
console.dir(test.val);
<script src="https://cdn.jsdelivr.net/npm/monet#0.9.0/dist/monet.min.js"></script>

Related

I can't change a variable value in object.entries foreach from parent scope

I want to get a boolean from an Object.entries(b).foreach but I don't know how to retrieve it.
Like :
const body = {a:1,b:2}
function handle() {
let myBool = true;
Object.entries(body).forEach(([key, value]) => {
myBool = false;
});
return myBool;
}
So, that's always return true, I tried something like
const body = {a:1,b:2}
function handle() {
return Object.entries(body).forEach(([key, value]) => {
return false;
});
}
But it doesn't work.
it's probably a lack of understanding JS, can you give me a hint ?
Unlike Array.prototype.map(), Array.prototype.forEach() always returns undefined and is not chainable. The typical use case is to execute side effects at the end of a chain.
More details here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

Confusion about Flow error wrt Filter function

In Javascript I've written code for a reducer to handle the adding & removing of strings from an Array. This code works fine:
const taskId: string = action.payload;
let taskIds: ?Array<string> = state.selectedTaskIds;
if (!taskIds) {
taskIds = [taskId];
} else if (taskIds.includes(taskId)) {
if (taskIds.length === 1) {
taskIds = null;
} else {
const idx = taskIds.indexOf(taskId);
taskIds = [...taskIds.slice(0, idx), ...taskIds.slice(idx +1, taskIds.length)];
// DNW: taskIds = taskIds.filter((selTaskId: ?string) => selTaskId !== taskId);
}
} else {
taskIds.push(taskId);
}
What doesn't work is the commented out line prefixed with "DNW" that is supposed to accomplish what the 2 lines of code above it do. The Flow error message I get is:
Missing type annotation for T. T is a type parameter declared in
array type [1] and was implicitly instantiated at call of method
filter [2].Flow(InferError)
I've tried several things but couldn't resolve it, which is why I used the other approach. But I am most curious what this error message is telling me and how to resolve it.

How to give a function a return value when the return is inside an if statement

I had a function that was initially like this, and worked:
productGroupPages(): PDF.Page[] {
const descriptions = {};
this.areas.forEach(area => descriptions[area.id] = area.description);
return this.json.engagementAreas.map(area => {
return this.productGroupPage(area, descriptions[area.id]);
});
}
Basically everything isnt wrapped in the for each.
I had to alter to the function as i wanted to go through a for each, to then use an if statement so i would only return values that actually contained a certain value, the result is that my return statement is too early, I cant move it to the end because of the for loop and im struggling to find out how I can get past this,
this new function:
productGroupPages(): PDF.Page[] {
const descriptions = {};
let pages = {};
console.log('this .json' + JSON.stringify(this.json))
this.json.engagementAreas.forEach(area => {
descriptions[area.id] = area.description
if (area.engagementTypes.length !== 0) {
return this.json.engagementAreas.map(area => {
return this.productGroupPage(area, descriptions[area.id]);
});
}
})
}
I tried creating a variable, an array or object and equaling that to the return value and then returning that near the end of the scope but this wouldnt let me it said the return type was wrong.
Any help would be greatly appreciated.
I think your initial code, with the forEach and map separated was very clean!
The problem with your new code, is that using return inside .forEach() does not make sense in javascript ;) Whenever you return from .forEach(), javascript just ignores the result:
let result = [1,2,3].forEach(x => {
return x * 2;
});
result // undefined
I think you wanted to only return some area-s, which you can do with array.filter() like this:
productGroupPages(): PDF.Page[] {
const descriptions = {};
this.areas.forEach(area => descriptions[area.id] = area.description);
return this.json.engagementAreas
.filter(area => area.engagementTypes.length !== 0)
.map(area => {
return this.productGroupPage(area, descriptions[area.id]);
});
}
I hope that is actually what you meant to do, and if so, I hope this works :)

Maybe monad sample code in javascript explanation

I'm starting or trying to learn functional programming monads.
So the first is Maybe. I'm trying to convert the code with maybe monad.
function(fieldName, vals, fields) {
var newValue = vals[fieldName];
if (typeof(newValue) == 'undefined') {
var elemFrom = fields[fieldName];
if (elemFrom) {
newValue = fields[fieldName]
}
}
if (typeof (newValue) != 'undefined') {
return newValue
}
}
Here I have a bunch of checks for undefined which i think is good use of monay.
My problem is that I read that you pass value to the maybe monad and map function.
However in my case I replace the value inside the monad.
If I pass null the map method will do nothig since the value is undefined.
I'm not using a framework, i want simple implementation so I can understand it.
Should I add "else" method in the maybe monad class (function).
I have the opposite case "Do something if the value is undefined"
Can you suggest how to solve the issue
Thank you
So the function you posted could be rewritten as
const f = (a, b, c) => b[a] === undefined ? c[a] : b[a];
It isn't clear to me that this needs to be a function at all rather than being inlined wherever you want to use the relevant object properties, but maybe you're partially applying it or something, I'm not judging.
As for Maybe, a (very simple) implementation might look something like this:
class Maybe {
static of (value) {
return new Maybe(value);
}
// Proper solution here should be recursive to handle
// nesting properly, but I'm lazy
static equals (a, b) {
return a.chain(x => x) === b.chain(x => x);
}
constructor(value) {
this._value = value;
}
map (f) {
// Does not distinguish null from undefined, but YMMV. Note
// that if the Maybe value is null or undefined we never touch
// f, that's the null propagation thing.
return this._value == null ? this : new Maybe(f(this._value));
}
chain (f) {
const result = this._value == null ? this : f(this._value);
console.assert(result instanceof Maybe);
return result;
}
}
Now we can test that it obeys the Monad laws:
const a = 3;
const f = x => Maybe.of(x * x);
Maybe.of(a).chain(f) === f(a) // left identity
Maybe.equals(Maybe.of(5).chain(Maybe.of), Maybe.of(5)); // right identity
And that it's a valid Functor
Maybe.equals(Maybe.of(3).map(x => x), Maybe.of(3)); // identity
Maybe.equals( // composition
Maybe.of(3).map(x => x + 2).map(x => x * 3),
Maybe.of(3).map(compose(x => x * 3, x => x + 2))
);
Sweet.
So now, to your function. It would be rewritten as
const f = (a, b, c) => {
return b[a] === undefined ? Maybe.of(c[a]) : Maybe.of(b[a]);
}
Perhaps you see now the reason for my confusion, Maybe isn't really saving you much here. But if I were using Maybe I'd rewrite the whole thing like this:
const or = (a, b) => {
return Maybe.of(a == null ? b : a);
}
And then I would just pass in the property accesses:
const obj1 = { a: 2, c: 3 };
const obj2 = { b: 4 };
const prop = "a"
const result = or(obj1["prop"], obj2["prop"]); // Maybe(2)
Update
Credit to #Bergi in the comments reminding me about Alternative. You could add a method to the Maybe class above like so:
alt (x) {
if (!(x instanceof Maybe)) {
throw new TypeError("Expected a Maybe");
}
return this.chain(x => x) == null ? x : this;
}
// semantics
Maybe.of(null).alt(Maybe.of(3)); // Maybe(3)
Maybe.of(2).alt(Maybe.of(4)); // Maybe(2)
// usage
Maybe.of(obj1[prop]).alt(Maybe.of(obj2[prop]));
Note that this doesn't totally satisfy as an implementation of Alternative (you'd also need a zero/empty method) but you can read here and here for more details. This is probably the best replacement for the function you posted.

Prevent getting error if variable is undefined?

I need to replace imagesrc with the value stored in this object. However when I run:
if(data['results'][res]['entities']['media']["0"]["media_url"]) {
imagesrc = data['results'][res]['entities']['media']["0"]["media_url"];
}
I get the error:
Cannot read property '0' of undefined
How can I run my condition so that I don't get errors if something is undefined?
if (data['results'][res]['entities']['media']["0"] == undefined
|| data['results'][res]['entities']['media']["0"] == null) {
...
}
you can place your code inside a try catch block and examin error message.
You could write a function that walks the object tree and returns undefined as soon as it hits an undefined property:
function safeGetData(obj, names)
{
for (var i = 0; i < names.length; ++i) {
if (typeof obj === "undefined") {
return undefined;
}
obj = obj[names[i]];
}
return obj;
}
You can use it like this:
var imagesrc = safeGetData(data,
["results", res, "entities", "media", "0", "media_url"]);
I’m a fan of using short circuit evaluation for these kinds of situations:
items && items[val] && doSomething(items[val])
Some people might be repulsed by this, but I think it’s a nice and readable way to express something that should only be evaluated if certain conditions are met.
In this case, we’re actually chaining two short circuit evaluations. First, we determine whether items has a defined value. If it undefined, then the rest of the expression is moot, so we won’t even bother to evaluate it. AND if it is defined, then let’s check for the existence of some property that we’re interested in. If it’s undefined, then bail out. AND if it’s true, we can go ahead and evaluate the rest of the expression.
I think it’s a lot easier to reason through at a glance than:
if (items) {
if (items[val]) {
doSomething(items[val])
}
}
Ternary operators work similarly:
items
? items[val]
? doSomething(items[val])
: alert(‘The property “‘ + val + ‘“ has not yet been defined.’)
: alert(‘You have not yet defined any items!’)
It's an old topic, I know. It's just to add my 2 cents.
I'm definitely not a javascript "guru", but here's one of my old attempts. It relies upon a couple of new ecmascript 6 features and it's going to approach the problem in a more "functional" way:
const prop = (...arr) => obj => arr.reduce((acc, v) => acc && acc.hasOwnProperty(v) ? acc[v] : undefined, obj)
And some tests in order to show how it should work:
describe('unit - prop', () => {
const event = {
record: {
sns: {
subject: 'Hello',
message: '<div>Welcome!</div>'
}
}
}
it('property exists', done => {
const value = prop('record', 'sns', 'subject')(event)
expect(value)
.to
.be
.equal('Hello')
done()
})
it('property does not exist', done => {
const value = prop('record', 'bad', 'subject')(event)
expect(value)
.to
.be
.undefined
done()
})
})
Does it make sense?

Categories