In week 3 of Code 201, our project was BusMall – think Sky Mall for people trapped on a long bus commute. Something of a familiar issue to Seattle commuters! From the programming end, it was an exercise in tracking and storing data persistently, then using Chart.js to render results. Object constructors, event listeners, randomizing functions, and more…
The project: http://peterbreen.github.io/bus-mall/
The use case, summarized, is as follows: Present the user with 3 randomly selected products and have them pick which one they like best of the three. Repeat a given number of times and when the right criteria have been met, use Chart.js to render a chart showing number of times displayed, clicked, and the conversion rate for each product – and keep the data stored persistently so you can see lifetime results (persistent per-computer using localStorage, we’re not using a db server)
I started with an object constructor to create objects with all the key:values that I’d need throughout the whole process – and as they were created, pushed these objects into an array for the rest of the process.
There was some variation in the class in how we randomized our three choices; I chose to select my products by making an array of numbers – equal in length to my array of objects (where each object represented one product). Next, I used a shuffle function to shuffle the array of numbers (using the Fisher-Yates shuffle) and splicing out the first three – at which point I went back to my array of objects and displayed the three objects indicated – say my number shuffle gave me 5, 8, and 12, I’d display the products in the array at index 5, 8, and 12. By keeping my object array in a known order, I could ensure proper display and click-tracking, and by using a number array I could shuffle, use, discard, and repeat I could ensure random selections for every iteration.
Some of my classmates went with a solution that then repeatedly looped through their array of objects directly to select an object, checking to see if it was a duplicate and discarding, and then looping again until until three unique indices were displayed – no matter how many times that loop had to execute to get there. Potentially, that could take a very large number of loops to return three unique values; on the other hand it was a lot simpler to write the code because it didn’t rely on a secondary array of numbers, so depending on circumstances it’s good to know both approaches.
Relatively speaking tracking clicks/display was pretty simple; if I knew a product was displayed I just did a displayed++ on that particular value inside the object, and the same with clicks onclick from my event handler, which proceeded to subsequently clear the three displayed choices and call the function to start the process again to pick three new random ones and display them.
There was also a global click count being tracked; this was to meet the MVP requiring 25 product choices before asking the user if they wanted to continue (letting them choose 10 more times) or go direct to the results. Either way, eventually all the click and display data was compared to produce a conversion rate (times displayed/times clicked) and then those 3 numbers were fed into Chart.js for each product.
Chart.js, to me, was almost too literally a binary: it either worked perfectly or it failed completely. It wasn’t exactly helpful in debugging, either, but eventually I fed it the right data:
Last, and … well, okay, probably least, we dropped Animate.css into the mix to make things a little more interesting. I’ve got mixed feelings about it – these are super easy and that’s good, but so many of them shouldn’t ever go within ten miles of a website meant for public use. Still, they aren’t all bad. I put in an easter egg into this project: Hover over the page title H1 and see a realistic interpretation of taking a local bus in Seattle!
Overall, the big takeaways for me here were handling multiple different potential situations inside the code and creating multiple functions – and they were directly related to one another.
Multiple functions, where each was fairly limited and only did one specific task, was both useful and following best practices – instead of One Function To Rule Them All, I’d call multiple functions in sequence as needed – this meant I could more easily handle different situations like “user chooses to see results immediately after 25” or “user chooses to do 10 more and then see results” without rewriting/duplicating code. It also made debugging a lot simpler.
That led directly into handling different situations in code – and made it pretty easy – if this is the user’s first time choosing products, localStorage will be empty. Don’t throw an error, just display their session results and store those in localStorage for next time. Then if it’s their second time, check to make sure localStorage exists and if so, pull those values out (JSON.parse to the rescue), add the session values, display the combined total and then save that combined total to localStorage(JSON.stringify to the rescue) for the next time, and so on. How many times can I type localStorage in one paragraph? But by using carefully limited functions, it was easy to write a human-readable if/else statement which means the code works and the code is understandable for a programmer to work with – the optimal outcome!