DEV Community

Cover image for 5 Programming Patterns I Like

5 Programming Patterns I Like

John Stewart on May 20, 2019

In this post I get into some patterns I try to use while programming. These patterns are observations I've made about myself recently while working...
Collapse
 
nepeckman profile image
nepeckman

Personally, my favorite way to do JavaScript switches is to isolate them to their own function. If you're doing a simple dispatch, a function is already an appropriate abstraction, and return gives you the ability to avoid the break mess.

// Object literal
function createContent(contentType){
   const contentTypes = {
     post: Post,
     video: Video,
     default: Unknown
   };
   const createType = contentTypes[contentType] || contentTypes['default'];
   return createType(); // or new createType() if its a class constructor
}

// Switch
function createContent(contentType){
  switch(contentType){
    case "post": return Post() // or new Post() if its a class constructor
    case "video": return Video()
    default: return Unknown()
  }
}

Utilizing the switch statement also dodges the biggest drawback of the object literal: the location of the object. If you put the object literal in your dispatch function, you're creating the object every time you run the function. If you create the object once outside of the function, now your dispatch logic is in a different place than the actual dispatch call.

I wish JavaScript had a built in pattern matching construct, but a well used switch statement isn't all that bad.

Collapse
 
geompse profile image
Geompse

Example with no object creation in the function:

// Object literal
const createContent = function(contentType){
   const contentTypes = createContent.contentTypes;
   const createType = contentTypes[contentType] || contentTypes.default;
   return createType(); // or new createType() if its a class constructor
}
createContent.contentTypes = {
  post: Post,
  video: Video,
  default: Unknown
};
Collapse
 
ecancino profile image
Eduardo Cancino • Edited

I love this solution, very simple;

I usually do this:

const  fromTypes = (types, def = null) => type => (types[type] || def)

const createContent = fromTypes({ post: Post, video: Video }, Unknown)

createContent('post')

Thread Thread
 
jaytailor45 profile image
Jay Tailor

This is how redux architecture works ;)

Collapse
 
thejohnstew profile image
John Stewart

I like that switch versus the one I wrote. :D

I recently discovered pattern matching via Scala and enjoyed using it!

Collapse
 
evanplaice profile image
Evan Plaice

Why not wrap the whole thing in one function. Use it as a closure to store the type data then return the switch function to access it?

Collapse
 
kleene1 profile image
kleene1

Cool post ! :)

Collapse
 
keevcodes profile image
Andrew McKeever

Love seeing these posts! I just recently used a switch in my code and didn't like the overall bulk the switch created. Nice ideas on how to clean it up!

Collapse
 
vycoder profile image
yev

As a personal preference I just try to avoid nesting in any form as much as possible because it really obscures the logic.

Say, this code snippet,

let result = null;
if (conditionA) {
  if (conditionB) {
    result = "A & B";
  } else {
    result = "A";
  }
} else {
  result = "Not A";
}

into:

function getResult() {
  if (!conditionA) {
    return 'Not A';
  }
  if (conditionB) {
    return 'A & B';
  }
  return 'A';
}

or even:

function getResult() {
  if (!conditionA) {
    return 'Not A';
  }
  return conditionB ? 'A & B' : 'A';
}
Collapse
 
adam_cyclones profile image
Adam Crockett 🌀 • Edited

Everything else looks fine but the nested turn ops.. not for me, clarity can go a long way over terse for the sake of others. Besides there are alternatives to nested conditionals. Boolean array .every or .some spring to mind.

// similar to x && y
const criteria = [
   user === 'logged-in',
   hasCardDetails
]
if ( criteria.every(isTrue => isTrue) ) {
   // do a thing if all are true
}
// similar to x || y
const criteria = [
   user === 'logged-in',
   hasCardDetails
]
if ( criteria.some(isTrue => isTrue) ) {
   // do a thing if some are true
}
Collapse
 
adamgerthel profile image
Adam Gerthel

Clever! Gonna remember this one

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀 • Edited

Your most welcome 😄, this is functional programming at it's finest. The only thing I don't like is the need to return the result by lambda. I haven't tried it but perhaps the Boolean constructor could sit where isTrue => isTrue is currently? I am afk so can't test.

Thread Thread
 
adamgerthel profile image
Adam Gerthel

I haven't tried it but perhaps the Boolean constructor could sit where isTrue => isTrue is currently?

This sentence went "swoosh" over my head.

I also had to google what a lambda was - had never heard that name for an anonymous function before (but I'm an autodidact which could explain it..) 😂

Could you elaborate in an ELI5 way? :D

Collapse
 
idanarye profile image
Idan Arye

3. One loop two arrays

Is reduce really necessary here? Wouldn't it be so much clearer to do this with a forEach?

const exampleValues = [2, 15, 8, 23, 1, 32];
const truthyValues = [];
const falseyValues = [];
exampleValues.forEach(exampleValue => {
        if (exampleValue > 10) {
            truthyValues.push(exampleValue);
        } else {
            falseyValues.push(exampleValue);
        }
});

5. Nested ternaries

The same flow you used in the ternaries can be applied to the if statements:

let result = null;
if (!conditionA) {
    result = "Not A";
} else if (conditionB) {
    result = "A & B";
} else {
    result = "A";
}

And if you used the same flow as in your if statements but with ternaries, they'd also look weird:

const result = conditionA
    : (
        conditionB
        : "A & B"
        ? "A"
    )
    ? "Not A";

Also, I find ternaries formatting to be more readable this way:

const result =
    !conditionA ? "Not A"
    : conditionB ? "A & B"
    : "A";
Collapse
 
adamgerthel profile image
Adam Gerthel

I think reduce is nicer because it's always clear that an Array.reduce constructs "something" from an array. The whole statement includes everything you need to know (i.e. the final argument is the base value) as opposed to declaring the arrays before the loop. No need to look outside the reduce for answers since it's all there.

Reduce can be a little intimidating in the beginning but once you get the hang of it it's wonderful and makes a lot of sense.

Collapse
 
idanarye profile image
Idan Arye

So it's better because you can put it in a single statement, even though you now have to address the arrays as array[0] and array[1] instead of by name?

Thread Thread
 
adamgerthel profile image
Adam Gerthel

You don't! You could actually do this instead:

const [truthyValues, falseyValues] = exampleValues.reduce(([truthyValues, falseyValues], exampleValue) => {
  if (exampleValue > 10) {
    return [[...truthyValues, exampleValue], falseyValues];
  }

   return [truthyValues, [...falseyValues, exampleValue]];
}, [[], []]);

I've spread the array in the return statement because I wasn't too fond of the push along with having to return the two arrays.

Another one, using an object instead of two arrays would be:

const { truthyValues, falseyValues } = exampleValues.reduce((result, exampleValue) => {
  if (exampleValue > 10) {
    result.truthyValues.push(exampleValue)
  } else {
    result.falseyValues.push(exampleValue)
  }

   return result
}, { truthyValues: [], falseyValues: []});

Which in my opinion is probably the easiest one to read.

Thread Thread
 
idanarye profile image
Idan Arye

I've spread the array in the return statement because I wasn't too fond of the push along with having to return the two arrays.

Which is how reduce is meant to be used, and why it is needed in functional languages that deal with immutable data. Otherwise you are just abusing it as a loop construct to feel functional.

But I wouldn't recommend this style in Javascript, because arrays are not linked lists and this becomes O(n2).

Thread Thread
 
adamgerthel profile image
Adam Gerthel • Edited

I don't have a computer science background so what you're referring to is not something I can easily relate to. I have a vague idea of the concept of linked lists, but I don't understand the big O notation (entirely new concept to me).

Do you mean that it's less performant than needed? Because I'm creating a new array which contains two arrays both of which will have to be looped due to using the spread operator as opposed to a push (which would not need to loop the whole array)?

Sorry if I'm not very clear, I'm simply not very good with these concepts and I'm genuinely interested in understanding what you're saying.

Update: Ok, I think I understand now.. You're saying that my solution with the spread operator will decrease in performance for each value in the exampleValues array, which is a bad practice. And it's O(n2) (opposed to O(n)) because I'm doing it twice?

Thread Thread
 
idanarye profile image
Idan Arye

O(n2), not O(n2). As in - O(n*n). As the array get bigger, the spread operator takes more and more time to perform because it needs to copy more values, as opposed to push which only needs to add one value (and sometimes increase the size, which takes longer - but it doesn't do it on each push)

Thread Thread
 
adamgerthel profile image
Adam Gerthel

O(n2), not O(n2). As in - O(n*n)

I'm aware, I just don't know how to do it on my keyboard. And yes, I do realise there's a major difference and it shouldn't be trivialised..

Anyway, thanks - I learned something new today :)

Thread Thread
 
idanarye profile image
Idan Arye

dev.to does it for you with the ^ operator - so O(n^2) becomes O(n2).

Collapse
 
zegio profile image
Tony

HA! I posted the almost the exact same else-if logic before realizing you had!

Collapse
 
karuppasamy profile image
Karuppasamy M

Personally, I don't like the nested ternary conditions.

const result = !conditionA
  ? "Not A"
  : conditionB
  ? "A & B"
  : "A";

Instead of the nested if..else, I would prefer the "Early assignment" like "Early Exits"

let result = null;
if (conditionA) {
  if (conditionB) {
    result = "A & B";
  } else {
    result = "A";
  }
} else {
  result = "Not A";
}

// instead

let result = "";

if(!conditionA) result = "Not A";
if(!conditionB) result = "A"
result = "A & B"
Collapse
 
johncip profile image
jmc

Nice post!

Re #1: Rubocop calls them "guard clauses" and I tend to prefer that style as well.

But if there are only two branches, I sometimes prefer the symmetry of if / else. Especially when if is an expression (as it is in Ruby).

Either way, IMO it's usually clear enough when input validation is being done, however it gets written. It's one of those places where obscuring the intent takes real talent :P

Re: #2: agree with @nepeckman that putting the switch statement in a function is usually a good option. Especially if multiple cases map to the same output value, because there you can use switch fallthrough to "stack" the tests and avoid repeating yourself.

Re: #3: reduce (fold) is so powerful 😍... map and filter can both be written in terms of it, and many more things as well. Many "indexed" loops can be translated into a function folded over a range of integers.

Also, I haven't seen it formalized, but it seems like a lot of tail-recursive code can be translated to "reducer style" as well -- it's a good option if the inputs can be easily generated as some sort of collection.

If the fold implementation allows early termination (a la Clojure's reduced, it becomes even more useful.

Collapse
 
johncip profile image
jmc • Edited

Oh yeah, in case you haven't seen it in action, you might be interested to know that a side-effect-free series of map calls can be refactored similar to your #3. map distributes across function composition, so

map(g, map(f, coll)) == map(compose(g, f), coll)

The latter form only walks the collection once, obviously. You can compose the functions manually or borrow compose from a library like Ramda.

Collapse
 
stm555 profile image
Seth Thornberry

Part of our coding standard is no acronyms (with a very few specific exceptions for industry terms). It is amazing how much easier it is to read code from 5+ years ago when it isn't littered with little three letter shibboleths.

Collapse
 
moopet profile image
Ben Sinclair

I think nesting ternaries makes things more difficult to change later without scratching your head. The linked article says it simplifies the conventional if

const withIf = ({
  conditionA, conditionB
}) => {
  if (!conditionA) return valueC;
  if (conditionB) {
    return valueA;
  }
  return valueB;
};

but it doesn't go all the way:

const withIf = ({
  conditionA, conditionB
}) => {
  if (!conditionA) return valueC;
  if (!conditionB) return valueB;
  return valueA;
};

At this point the if and the ternary are nearly identical, with the exception that the if statement blocks can contain anything they like without breaking the layout. Imagine if instead of returning a simple value they were all returning a long string or a calculation? I mean, you can say that these sorts of things should be factored into their own functions, but that's true of both techniques.

Collapse
 
josefjelinek profile image
Josef Jelinek • Edited

There is a very unfortunate drawback of using a function for "if" or ternary... both branches are always evaluated and that often means dereferencing null or undefined...

Collapse
 
denishowe profile image
Denis Howe

Early exits keep indentation sane which contributes a lot to readability.

Nested ternaries are great and very clear if you lay them out like:

const result =
  !conditionA ? "Not A" :
  conditionB ? "A & B" :
  "A";

so the structure of each line is exactly the same.
(Don't try this in PHP by the way - you need parens to fix the broken associativity).

Collapse
 
qcgm1978 profile image
Youth

You're right. I have found it is exactly easy to understand the ternaries because the priority level is default satisfying what I want it to be. But One loop two arrays I don't agree. I code the following code and it is more elegant:

const validTrolley = result.filter(item => item.inventoryCount && item.putShelvesFlg)
const invalidTrolley = result.filter(item => !(item.inventoryCount && item.putShelvesFlg)).map((item, index) => {
            item.isInvalid = !index;
            return item;
          })
Collapse
 
erenyatkin profile image
Ahmet Eren Yatkın

I love nested ternaries, shortens the code. A real world example, simple Vue-router meta controlling;

    const accessControl =
      to.meta.abKey === 'default'
        ? true
        : to.matched.filter(x => abKeys.includes(x.meta.abKey)).length > 0
        ? true
        : false;
Collapse
 
guico33 profile image
guico33

cond1 ? true : cond2 ? true : false

is the same as

cond1 || cond2

Collapse
 
zegio profile image
Tony

I really appreciate and identify with this article.

But... Not the nested ternaries. The example used as the "bad nested if" feels to me like those commercials for cracking eggs and how no one can do it (meaning, it's actually easy, but we want to sell you a thing)

First, let's kill the unneeded semicolons, because, yuck. Second, reverse the first logic and use else if for the second and you have yourself some pretty simple logic that can also be multiple lines long... but... for the sake of this example we don't need curly braces either. So apples to apples if code would be below. Honestly, the nested ternary functions I've seen are a HUGE mess, not at all as simple as the example provided. Especially while returning JSX and returning blocks of JSX-HTML...shudder

if (!conditionA)
  result = 'Not A' 
else if (conditionB)
  result = 'A & B'
else
  result = 'A'

Multi-line Version

if (!conditionA) {
  result = 'value: Not A' 
  console.log('not A')
} else if (conditionB) {
  result = 'A & B'
  console.log('value: A & B')
} else {
  result = 'A'
  console.log('value: A')
}
Collapse
 
marclundgren profile image
Marc Lundgren • Edited

When using reduce, I prefer to always return a new pure array rather than pushing to an existing array. Keeping reduce stateless has resulted in less debugging for me.

const exampleValues = [2, 15, 8, 23, 1, 32];
const [truthyValues, falseyValues] = exampleValues.reduce(([truthy, falsey], value) => {
  if (value > 10) {
    return [truthy.concat(value), falsey];
  } else {
    return [truthy, falsey.concat(value)];
  }
}, [[], []]);
Collapse
 
verrigo profile image
Radu Caprescu

I'm sorry, but if you like nested ternaries you are either a bad man or someone who didn't have a 4 or 5 nested ternary that made you scream with pain. Or both :D

Collapse
 
thejohnstew profile image
John Stewart

😅

I haven't done 4 or 5 nested. That seems a bit much lol

Collapse
 
verrigo profile image
Radu Caprescu

Well, I have PTSD with using ternaries. I once did some work in Jasper Reports and their scripting language used only ternaries. Now every ternary that is longer than 1 line (max 2 at the most) gives me the creeps and is an instant code smell for me

Collapse
 
geompse profile image
Geompse

Nested ternaries can be tricky for their operator not having the same priority between programming langages :

a='a',b='b',c='c',d='d',e='e',a?b:c?d:e; // ==='b'
$a='a';$b='b';$c='c';$d='d';$e='e';echo $a?$b:$c?$d:$e; // ==='d'

Also using spaces or multiple lines may mislead the programmer, suggesting priority :

a?b:c ? d : e;
a?b:c
  ?
    d
  :
    e;

To me, it is always better to use parenthesis, on one line of code. If the line goes too long you should use variables (as in pattern 4 "No 'foo' variables") :

const result1 = !conditionA ? "Not A" : (conditionB ? "A & B" : "A");
const result2 = (!conditionA ? "Not A" : conditionB) ? "A & B" : "A";

Still my preference goes to a non nested if/elseif/else blocks (i find it a lot clearer) :

if (!conditionA) {
  result = "Not A";
else if (conditionB) {
  result = "A & B";
} else {
  result = "A";
}
Collapse
 
pinotattari profile image
Riccardo Bernardini

I use mostly Ada and very sparingly JavaScript, but...

(1) I agree with the "no foo" suggestion (although when I am really in a hurry I throw in a "Tmp" with the idea of choosing a better name later...). I always try to use fully readable names for my variables and functions.

(2) About the nesting of ternary operator, my preference goes to the "ternary Ada style," similar to the ?: but with full verbose "if", "then", "else" and, if needed, "elsif" :-) oh, yes, and "case..when" :-)

Collapse
 
xtofl profile image
xtofl • Edited

These patterns are so useful in day to day work!

In the 'one loop two arrays' pattern for partitioning I like to emphasize what is the essential, minimal difference in each condition. In this case, it's really just 'what array do I append to?'

var target =
  arrays[
    predicate(example) ? 1 : 0];
target.push(example);
Collapse
 
kiransiluveru profile image
kirankumar • Edited

For nested ternaries we can use self-executable loops:

{ (() => {
if(x) {
//do something...
console.log(''x')
}
if(y) {
//do something...
console.log(''x')
}
if(z) {
//do something...
console.log(''x')
}
}
)()
}

Collapse
 
steffenpedersen profile image
Steffen Pedersen

Are you thinking about specific programming patterns, when you are programming? Like "Oh, I gotta use 1. Early exits right now." 😀

Collapse
 
thejohnstew profile image
John Stewart

:D

Definitely not, just things I noticed I do sometimes.

Collapse
 
assertnotnull profile image
Patrice Gauthier

Instead of switch to object literal I suggest the facade pattern. Going with facade pattern, your IDE will be able to resolve function definition. If this object resolution grows in complexity it will be harder to understand why this generic function refers to and only your debugger can help you.

Collapse
 
booradley profile image
boo radley

The details of this would be an interesting topic for a follow-up article.

Collapse
 
thejohnstew profile image
John Stewart

Googled and found this:

I like it.

Collapse
 
vzuburlis profile image
Vasilis Zoumpourlis

The Early Exits I know them as "Quick Fails" and its the best patter I learned recently.

Collapse
 
thejohnstew profile image
John Stewart

I like that name also

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

Awesome article mate 👌

Collapse
 
elanandkumar profile image
Anand Kumar

I liked the first two and use very often.

I kind of feel nested ternary is more confusing though mileage varies person to person.

Collapse
 
tremainebuchanan profile image
Tremaine Buchanan

Great post! We both use the first concept you mentioned in the post. I'll explore the others and see how I can incorporate them also.

Collapse
 
thejohnstew profile image
John Stewart

Thanks!

Collapse
 
thejohnstew profile image
John Stewart

Where are these terms?

Collapse
 
webgodo profile image
Mohsen

Such an incredible idea is in 'bifurcate'

Collapse
 
moonpatroller profile image
moonpatroller

Omitting the else and else ifs when you do the early return always seems dangerous.

Collapse
 
shlommi profile image
shlommi

hi,
someone can give me advice which book about programming patterns to buy ? I'm interested in this.
thanks

Collapse
 
myterminal profile image
Mohammed Ismail Ansari

It's very relatable, have been doing almost all of them in my code (except the two arrays pattern, which is a great idea as well).

Collapse
 
nuculabs_dev profile image
Nucu Labs

JavaScript X.X