Week 1: Helping the Bookseller

Week 1: Helping the Bookseller

 

The Challenge

The idea for this post came to me when I completed the Codewars challenge: ‘Help the Bookseller’.

I’ve been doing Codewars Katas for a while now, as part of my quest to learn computing coding and although I have completed quite a lot of Katas, I have found that the possible practical application of the solution often eludes me. However, I really liked the concept of ‘Help the Bookseller’ and think I can see some possible uses for knowing how to do something like this. I haven’t had to try out any of these use cases yet but I’m going to have a go at explaining my method and maybe this might help you too – if you ever need to do something similar!

Here is what the Kata asks us to do:

A bookseller has lots of books classified in 26 categories labelled A, B, … Z. Each book has a code c of 3, 4, 5 or more characters. The 1st character of a code is a capital letter which defines the book category.

In the bookseller’s stocklist each code c is followed by a space and by a positive integer n (int n >= 0) which indicates the quantity of books of this code in stock.

For example, an extract of a stocklist could be:

[pastacode lang=”javascript” manual=”L%20%3D%20%7B%22ABART%2020%22%2C%20%22CDXEF%2050%22%2C%20%22BKWRK%2025%22%2C%20%22BTSQZ%2089%22%2C%20%22DRTYM%2060%22%7D” message=”” highlight=”” provider=”manual”/]

You will be given a stocklist (e.g.: L) and a list of categories in capital letters e.g.:

[pastacode lang=”javascript” manual=”M%20%3D%20%7B%22A%22%2C%20%22B%22%2C%20%22C%22%2C%20%22W%22%7D” message=”” highlight=”” provider=”manual”/]

and your task is to find all the books in stocklist L with codes belonging to each category of M and to sum their quantity according to each category.

For the lists L and M of example you have to return the string (in Haskell/Clojure/Racket/Prolog a list of pairs):

[pastacode lang=”javascript” manual=”(A%20%3A%2020)%20-%20(B%20%3A%20114)%20-%20(C%20%3A%2050)%20-%20(W%20%3A%200)” message=”” highlight=”” provider=”manual”/]

Oh and if L or M are empty return an empty string.

OK this took me a while to understand. However, after re-reading it a few times I got what they were after:

Find all the items in list L which begin with each of the letters in list M and add together the total quantity of items that begin with each letter.

A path

The steps I reckoned this would take to achieve the required answer were:

  1. Check for empty values – if found – return an empty string.
  2. Reduce the items in stocklist (L) into an object (let’s call it totals) with key value pairs where the first letter of each item (the category) is the key, and the value is the quantity at the end of the string. If there is more than one item which starts with the same letter, then the quantities are summed, and the value is the total of these sums.
  3. Now you need to map through M and return the value of each category listed in totals, each pair surrounded in brackets. If there are no values for a particular item in M, then the value is 0.
  4. The final step is to format it as requested – with brackets around each pair and dashes in between pairs.

Here we go (using Javascript):

[pastacode lang=”javascript” manual=”%0As%20%3D%20%5B%22BBAR%20150%22%2C%20%22CDXE%20515%22%2C%20%22BKWR%20250%22%2C%20%22BTSQ%20890%22%2C%20%22DRTY%20600%22%5D%0Am%20%3D%20%5B%22A%22%2C%20%22B%22%2C%20%22C%22%2C%20%22D%22%5D%0A%0A%0A” message=”” highlight=”” provider=”manual”/]

  1. One way to check for empty an empty array is to check its length.

    [pastacode lang=”javascript” manual=”%20%20%20%20function%20stockList(s%2C%20m)%20%7B%0A%20%20%20%20%20%20%20if%20(s.length%20%3D%3D%3D%200%20%7C%7C%20m.length%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20return%20%22%22%3B%0A%20%20%20%20%20%20%7D” message=”” highlight=”” provider=”manual”/]

  2. Once we’ve ascertained we aren’t working with empty inputs we can get on to our second step: reducing the stocklist into a totals object.

    Reduce is a good method of creating an object with key-value pairs from an array, although by no means the only way.

    [pastacode lang=”javascript” manual=”let%20totals%20%3D%20s.reduce((accum%2C%20curr)%20%3D%3E%20%7B%0A%20%20%20%20if%20(!accum%5Bcurr%5B0%5D%5D)%20%7B%0A%20%20%20%20%20%20accum%5Bcurr%5B0%5D%5D%20%3D%200%3B%0A%20%20%20%20%7D%0A%20%20%20%20accum%5Bcurr%5B0%5D%5D%20%2B%3D%20%2Bcurr.split(%22%20%22)%5B1%5D%3B%0A%20%20%20%20return%20accum%3B%0A%20%20%7D%2C%20%7B%7D)%3B%0A” message=”” highlight=”” provider=”manual”/]

    Within the reduce function we check if the totals object we are creating contains the current item in s we are dealing with, and if it doesn’t we set it as a key and its value to zero. Then when we have created it (or if it already exists) we need to add the quantity of that item. This we can establish by separating the current item string into two parts (by splitting it where the space between the code string  and the amount is). The quantity to add is then the item at index 1 of the split array.

    If you run console.log(totals) at this point you would get this:

    [pastacode lang=”javascript” manual=”%7B%20B%3A%201290%2C%20C%3A%20515%2C%20D%3A%20600%20%7D” message=”” highlight=”” provider=”manual”/]

    This is good – we have our categories and the total number of items in these categories.

  3. To get our result and return it in the correct format, we can map over the input m. Inside the map function we can use a template literal to place each item in m with a corresponding value for that item from totals and surround each pair with brackets. If the item isn’t present in totals then the value for that item is zero.

    [pastacode lang=”javascript” manual=”let%20result%20%3D%20m.map((x)%20%3D%3E%20%7B%0A%09%20%20%20%20%20%20%20%20%20return%20%60(%24%7Bx%7D%20%3A%20%24%7Btotals%5Bx%5D%20%7C%7C%200%7D)%60%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A” message=”” highlight=”” provider=”manual”/]

    If you run console.log(result) at this point you would get this:

    [pastacode lang=”javascript” manual=”%5B%20′(A%20%3A%200)’%2C%20′(B%20%3A%201290)’%2C%20′(C%20%3A%20515)’%2C%20′(D%20%3A%20600)’%20%5D” message=”” highlight=”” provider=”manual”/]

    This is great we’re nearly there – we just need to format it as requested.

  4. To format the answer we can simply use:

    [pastacode lang=”javascript” manual=”result.join(%22%20-%20%22)” message=”” highlight=”” provider=”manual”/]

     

    This returns the items in result as a string separated by hyphens.

    And that’s it!

    A solution

    Here it is all put together:

    [pastacode lang=”javascript” manual=”function%20stockList(s%2C%20m)%20%7B%0A%20%20if%20(s.length%20%3D%3D%3D%200%20%7C%7C%20m.length%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20return%20%22%22%3B%0A%20%20%7D%0A%20%20let%20totals%20%3D%20s.reduce((accum%2C%20curr)%20%3D%3E%20%7B%0A%20%20%20%20if%20(!accum%5Bcurr%5B0%5D%5D)%20%7B%0A%20%20%20%20%20%20accum%5Bcurr%5B0%5D%5D%20%3D%200%3B%0A%20%20%20%20%7D%0A%20%20%20%20accum%5Bcurr%5B0%5D%5D%20%2B%3D%20%2Bcurr.split(%22%20%22)%5B1%5D%3B%0A%20%20%20%20return%20accum%3B%0A%20%20%7D%2C%20%7B%7D)%3B%0A%20%20let%20result%20%3D%20m.map((x)%20%3D%3E%20%7B%0A%20%20%20%20return%20%60(%24%7Bx%7D%20%3A%20%24%7Btotals%5Bx%5D%20%7C%7C%200%7D)%60%3B%0A%20%20%7D)%3B%0A%20%20return%20result.join(%22%20-%20%22)%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

  5. It can be simplified – for example steps 3 and 4 can be done in a single step.

    There are no doubt countless other ways to achieve the correct end result, but I feel this way is quite simple to follow and could have variations of the request incorporated quite easily. For example, if the result information was requested in a different format that would be straightforward to implement.