DEV Community

Cover image for Conditional Function Invocation without Conditional Statements in JS
Daniel Sasse
Daniel Sasse

Posted on

4 2

Conditional Function Invocation without Conditional Statements in JS

Reminiscing of Ruby's .send

Since I have begun exploring the world of Javascript and React in recent weeks, one of the things I have missed from the world of Ruby has been the .send method. Being able to call a method by way of a string or symbol argument (that could be stored in a variable) opens many doors for abstraction.

For those who are unfamiliar with Ruby's send method, I discuss it more in this blog post, but the gist of it can be seen below...(get it?)

“hello”.capitalize #=> “Hello”
“hello”.send(“capitalize”) #=> “Hello”
view raw send_example.rb hosted with ❤ by GitHub

While the amazingness of this may not be immediately apparent, I'll quickly reuse one of the example from the blog linked above. Here we are taking an array of instances, and are looking to create a new hash where the keys are specific property values of the instances, and the value for each key is the number of instances that have that property. There are a variety of ways to do this, but without send you would need two separate methods to perform this action for two different properties, however with send and the ability to pass in a string value that matches a property, we are able to use the same method for two different properties.

def self.count_vhs_rentals_by(attribute)
Rental.all.each_with_object({}) do |rental, vhs_hash|
vhs_hash[rental.send(“#{attribute}”)].nil? ? vhs_hash[rental.“#{attribute}”] = 1 : vhs_hash[rental.“#{attribute}”] += 1
end
end
Vhs.count_vhs_rentals_by(“vhs”) #=>
#{<Vhs:0x00007f8d5e2bf0f0 id: 17, serial_number: "MAX-luqovgdk8e7tl", movie_id: 9 => 1,
#<Vhs:0x00007f8d4f81a4a0 id: 29, serial_number: "BABA-t6k24krzqclts", movie_id: 13 =>2,
#<Vhs:0x00007f8d4f854538 id: 11, serial_number: "BABA-begvxe7ai0rc6", movie_id: 13 =>3}
Vhs.count_vhs_rentals_by(“client”) #=>
#{#<Client:0x00007f8d5e2ae6d8 id: 4, name: "Hank Jennings" =>1}
#<Client:0x00007f8d5e2be3a8 id: 7, name: "Lisa Wilkes" =>2}

Enter the world of Javascript

As I dive in to Javascript, I love how it allows you to store functions as variables and pass functions as arguments, however for a while it still felt like something was missing, since I was struggling to find a way to conditionally invoke functions without the unnecessary complications of if... or switch statements.

Recently I came up with a solution, capitalizing on Javascript's ability to store functions as values in an Object while working on the problem below:

In a project, I was provided access to a Log class that took in a string and output the string as a log message object that contained the message type (error, warn,notify), the timestamp, and the message text:

export class Log {
static error(msg){
let date = new Date();
let time = date.toLocaleTimeString();
return {type: 'error', msg: `[${time}] ERROR: ${msg}`}
}
static warn(msg){
let date = new Date();
let time = date.toLocaleTimeString();
return {type: 'warn', msg: `[${time}] WARN: ${msg}`}
}
static notify(msg){
let date = new Date();
let time = date.toLocaleTimeString();
return {type: 'notify', msg: `[${time}] NOTIFY: ${msg}`}
}
}
view raw js-log-class.js hosted with ❤ by GitHub

Immediately I felt the single silent tear and pang of nostalgia for Ruby's send once again. How nice would it be to be able to invoke the correct function by just including the message type as a string along with the message test!

Log.send("warn")(message_text)
Enter fullscreen mode Exit fullscreen mode

The Solution

While typing the same statement into my text editor wishfully pretending JS has learned to be as fluffy as Ruby in the last few days, I realized "warn" in this case could also essentially be thought of as a key in an object, and JS DOES have the ability to process variables to use their values in selecting a key:

const greetings = {
english: "hello",
spanish: "hola"
}
const langChoice = "spanish"
greetings.english //=> "hello"
greetings[langChoice] //=> "hola"

Since JS ALSO has the ability to store functions as keys in an object, this means that the three Log class methods that were defined earlier can each individually be stored into a logGenerator object under different keys. Any variable containing a string can then be passed to the object to load the desired function, and then invoke it with the desired arguments without the need for messy conditionals. See the comparison below:

// Using Conditionals
const newMessage = {type: "error", message: "You still haven't squished the bugs!"}
const generateLog = newMessage => {
if (newMessage.type === "warn" {
Log.warn(newMessage.message)
} else if (newMessage.type === "error" {
Log.error(newMessage.message)
} else if (newMessage.type === "notify" {
Log.notify(newMessage.message)
}
}
generateLog(newMessage)
// Without Conditional Statements
const newMessage = {type: "error", message: "You still haven't squished the bugs!"}
const logGenerator = {
warn: Log.warn,
notify: Log.notify,
error: Log.error
}
logGenerator[newMessage.type](newMessage.message)
// logGenerator[newMessage.type] loads the Log.error function which is then invoked
// with the argument of newMessage.message. Any of the Log functions can be called without
// any extra conditionals

As the conditional logic becomes for complex, or the number of possible values for the variable increase, the need for such a simple way to access a multitude of different functions becomes even clearer. Store the functions in an object, and use a variable to target the key of the function you wish to invoke.

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (2)

Collapse
 
webketje profile image
webketje • Edited

Object.keys makes the LogGenerator completely obsolete:

const msg = { type: 'notify', text: 'Hello World' };
Object.keys(Log)[msg.type](msg.text);
Enter fullscreen mode Exit fullscreen mode

as

class Log {
  static notify() {
    ...
  }
}
Enter fullscreen mode Exit fullscreen mode

is really just syntactic sugar for

function Log() {}
Log.notify = function() { ... }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dsasse07 profile image
Daniel Sasse

Thank you for the insight! I haven't had any experience with Object.keys() before, and my use of Class syntax in JS so far is still limited.

Thank you for the suggestion.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more