Pretty, don’t you think?

1 | /* |

What’s the easiest way you know of to tokenize an arithmetic expression in javascript? Let’s say you’re building a calculator application, and want this to happen:

1 | console.log( |

Before you reach into your npm module bag-o-tricks, realize that this can be done in one line of javascript using a secret feature of the string split method. Behold:

1 | '100-(5.4+2/3)*5' |

Excuse me? What’s that hot mess inside the `split`

function? Let’s break it down step by step using a few examples of increasing complexity:

`s.split(/-/)`

Pretty obvious: this splits the string `s`

anywhere it sees the minus sign symbol `-`

.

1 | '3-2-1'.split(/-/) |

`s.split(/(-)/)`

The only difference from the previous example is the enclosing parens in the regex, which creates a capturing group. Here’s the key point of the entire article: If the regular expression contains capturing parentheses around the separator, then each time the separator is matched, the results of the capturing group are spliced into the output array.

1 | '3-2-1'.split(/(-)/) |

`s.split(/(-|\+)/)`

This builds off the previous example by adding support for the addition symbol `\+`

. The backslash `\`

is required to escape the regex. The vertical pipe `|`

acts as an OR statement (match `-`

OR `+`

).

1 | '3-2-1+2+3'.split(/(-|\+)/) |

Hopefully, you now have all tools needed to understand `.split(/(-|\+|\/|\*|\(|\))/)`

. Hope that made sense! Let me know in the comments if you liked this article, or ping me on twitter!

One less understood idea among javascript engineers (unless you happen to be studying up for interviews) is the use of greedy algorithms. A greedy algorithm makes whatever choice seems best at the moment, and solves the subproblems that arise later. To use a visual metaphor, we put the result of each subproblem in a “bag” and then repeat with successively smaller subproblems. When the subproblem is empty (nothing left to do), we return the contents of the bag.

As it turns out, this strategy can lead to some very elegant solutions to practical problems. In the rest of this article, we’ll explore four seemingly different problems that have almost identical solutions (hint: they all use a greedy algorithm). In closing, we’ll take a closer look at the structure common to all four problems. Let’s dive in!

1 | You are given coins of different denominations and a total amount of |

Take a moment to consider how you’d do this before continuing… (answer is right below)

1 | /* |

We keep a “bag” of coins and recursively add coins to the bag that matches our selection criteria `(pick largest coin denomination that is < amount)`

. If the largest coin has value `C`

, we add `C`

to the bag and call `makeChange`

with `amount - C`

. This continues until the `amount`

is 0, and the bag of coins is returned.

A quick note on the expression `{ ...bag, ...{ [fn(array[0])]: matches } }`

since there’s a lot going on there. First of all, what does `{ ...a, ...b }`

mean? This is called object spreading. Think of it as smooshing together objects a and b to create a new object. So `{ ...bag, ...somethingElse }`

will combine the object `bag`

with object `somethingElse`

. In this case, `somethingElse`

is the object `{ [fn(array[0])]: matches }`

which is the new group we’re inserting into the bag.

I’ll also explain the difference between `{ [key]: value }`

and `{ key: value }`

. Those square braces signify computed properties. You can stick any expression between the square braces, and the value of that expression will become the value of the key. So for example `{ [1 + 1]: 2}`

would be the same as `{ 2: 2 }`

.

1 | Implement the "groupBy" function which takes an array A and a function F, |

Take a moment to consider how you’d do this before continuing… (answer is right below)

1 | /* |

Keep a “bag” of groups and recursively add groups to the bag that match our selection criteria `fn(x) === fn(array[0])`

. Then call `groupBy`

on the remaining elements, with the updated bag. This continues until the original array is empty, and the bag is returned.

Another classic problem is the activity selection problem.

1 | Imagine you are trying to schedule a room for multiple competing events, |

Take a moment to consider how you’d do this before continuing… (answer is right below)

1 | class Appointment { |

For our final example, we’ll consider the problem of grouping anagrams.

1 | Given an array of strings, group anagrams together. |

Take a moment to consider how you’d do this before continuing… (answer is right below)

1 | function collectAnagrams(words, bag = []) { |

So what do all of these problems have in common? For every iteration through the loop, we select a subset of items from the input and add it to the bag. The remaining items feed through to the next iteration of the loop as the next input. When the input is empty, we return the bag.

The following diagram might help clarify things, using our groupBy example:

If you’re more comfortable with pseudocode, here is the pattern we used in all of the previous examples:

1 | function bagItUp(things, bag = []) { |

What do you think? Have you solved similar problems at work or personal projects using greedy algorithms? Let me know in the comments below, or on twitter!

]]>