CodeEval Chardonnay or Cabernet

We are given in input a list of blank separated words, supposedly wine names, and, after a pipe, another word. We should consider the last word as a loose collection of characters. We should return each wine name that contains these characters, or the string 'False'. You could fine a fancier description on CodeEval, problem #221.

I have converted the provided samples in test cases for Python 3. I have added a fourth test because I found an ambiguity in the problem description that I wanted to clarify.
def test_provided_1(self):
    self.assertEqual('Merlot', solution('Cabernet Merlot Noir | ot'))

def test_provided_2(self):
    self.assertEqual('Chardonnay Sauvignon', solution('Chardonnay Sauvignon | ann'))

def test_provided_3(self):
    self.assertEqual('False', solution('Shiraz Grenache | o'))

def test_reversed(self):
    self.assertEqual('pinot', solution('pinot | to'))
As you can see in the latest test, the characters in the last word do not say anything about the required search order in the wine name. So 'pinot' is a match for 'to', because it contains both characters, even if in reversed order.

Given this specification, I thought the best way to solve the problem was putting in a dictionary the letters we want to search and their number, and then comparing them in the wine name.

Setting the dictionary
hints = {}
for c in data[1]:
    hints[c] = hints.get(c, 0) + 1
Being data[1] the result of splitting the input line on ' | ', I loop on each character. I try to get it from the dictionary. If it is not there, I force get() to return 0, instead of the default None. Then I increase the value returned by get(), increase it, and store it back in the dictionary.

Selecting the wines
result = []
for wine in wines: # 1
    for key in hints.keys(): # 2
        if wine.count(key) < hints.get(key): # 3
            break
    else: # 4
        result.append(wine)
1. Loop on wines, the list that contains all the words on the left to ' | ', split by the default single blank as separator.
2. Loop on all the characters in the dictionary initialized above.
3. Count the current character in the current wine. If there are less instances of it than expected, exit from the for loop through a break.
4. A nice Python feature. When the for loop is completed correctly, meaning no break has interrupted its execution, proceed to its else block, if any. Here it means that all the checks have succeeded, so I push the current wine in the result list.

Conditional join

If there is something in the result list, I should return it as a string, joining each wine name to the next on a blank. However, if the list is empty I should return a not-found message. As a C programmer, I am used to do it with the infamous ternary operator (?:). It does not exist in Python, but there is a mimic if-else construct:
return ' '.join(result) if result else 'False'
It could be read in this way: if result is not empty return the join on result, otherwise return the 'False' string.

Being this solution accepted by CodeEval, I have pushed test cases and the python 3 function source code to GitHub.

No comments:

Post a Comment