[006.3] Starting an App using Mori.js

An overview of what we will build

Subscribe now

Starting an App using Mori.js [12.29.2017]


Welcome back to our tutorial on functional programming in Javascript with Mori.js. Yesterday we did a quick dive into Mori's data structures and functions so that today we could shift into our small project.

So you may be wondering, what are we building? Well, we will be setting up a command line tournament bracket system. In this system, a user will input their list of contestants, then the app will construct the tournament brackets where then the user can input who is winning and losing. As the tournament progresses, the app will rebuild the brackets until and run the cycle again until we have a champion.

Here is a quick demo... (run command line demo here)

Now that you have an idea of what will be built, it's time to plan how we will architect this app.

Thinking functionally - Architecting our app.

Step number one, we need to take the time to plan our application and organize what our data structures will be. In this case, we know that we have one list of contestants with no other attributes. So a list of strings will be appropriate. It's also safe to say that this list will be worked on a lot and we will need a lot of random access to the values so a Mori vector seems the appropriate structure to act as a base from which our app will run.

So when we first start up the app, the user will be adding values to this home vector until they have entered all of their contestants. For testing sake, let's just set up some dummy working data.

const _ = require('mori')

const testData = _.vector('test', 'test1', 'test2', 'test3', 'test4', 'test5', 'test6', 'test8')


1. test
vs.         1. test1
2. test1
            vs.         1. test1        
3. test2
vs.         2. test2
4. test3
                        vs.         champion. test4
5. test4
vs.         3. test4
6. test5
            vs.         2. test4
7. test6
vs.         4. test6
8. test7


There, this can give us some kind of visual for how this data will be processed. Anyone familiar with how a tournament works knows that we will have to break this list up into brackets. So you may notice there are some fundamental assumptions with this bracket system. We're assuming the initial row will be an even numbered length and we're assuming the length won't be divisible by 3. To keep things simple, we won't worry about the divisible by 3 situation, but we can at least devise a way to handle the odd number situation. One easy way will be to just remove the last contestant to make the number of competitors even. In a more serious larger scale project, such an approach wouldn't be adequate but for our current needs, it should suffice.

Now we need to decide how we're going to process each bracket. We know that we will start with one vector, then build a new vector of the winners, then repeat again and again until we get to the final champion. However, we need to figure out how we are going to work through each list to get these winners into the new vector.

To do this, let's check out one plausible approach.

  1. Set up a recursive function that will use an incrementer and receive the list along with a new blank vector.
  2. Grab 2 items from the list at the incrementer index and the incrementer index + 1.
  3. allow the user to select the winner between the 2.
  4. If we're at the end of the list, return a new vector that has our list plus the new value.
  5. If we aren't at the end of the list, call this function again, passing it a new vector with the winner added at the end.

If this was a little hard to grasp, it's okay, here is a visual pseudo-example.

function runMatch (incrementer, list, newList) {
  const firstItem = _.nth(list, incrementer)
  const secondItem = _.nth(list, incrementer + 1)

  const winner = getUserPick

  switch(winner) {
    case firstItem:
      if (incrementer >= (_.count(list) - 2)) {
        return _.conj(newList, firstItem)
      } else {
        return runMatch(incrementer + 2, list, _.conj(newList, firstItem))
    case secondItem:
      if (incrementer >= (_.count(list) - 2)) {
        return _.conj(newList, secondItem)
      } else {
        return runMatch(incrementer + 2, list, _.conj(newList, secondItem))
      return runMatch(incrementer, list, newList)

This should give you an idea of how we can actually run our tournament. So to put all this into perspective, this is what we need from our app in the big picture.

  1. Gather input from the user into a vector.
  2. When done, check if the list length is even.
  3. If the list length is even, print the list as a bracket to the user.
  4. If the list is not even, remove the final contestant then print the list as a bracket to the user.
  5. Use the runMatch algorithm to process the list, extracting all winners into a new bracket.
  6. When this is done and you have one contestant left, end the program.

In the big picture this is simple enough and we can do this all this functionally. Tomorrow, you will see this in action. as we do just that.


Today has been shorter but necessary. We needed to get some idea of how we're going to architect our app so we can break things up into manageable independent functions. Be ready for tomorrow because it will be all about coding this out.