patternpythonMinor
Ordering an un-ambiguous scrambled date
Viewed 0 times
ambiguousscrambledorderingdate
Problem
A date is un-ambiguous when each number may be
decided to be day or month or year given the range
limitation of these numbers.
For example
@param
of the format X/Y/Z where X, Y, Z correspond to
day, month, year but not necessarily in this order.
@return The date in the order 'DAY/MONTH/YEAR'.
I am particularly interested in full input validation and nice error producing, so tell me any lack of clarity/corner case missed.
For example usage see the doctests included with the function:
```
def order_date(un_ambiguous_date):
"""
@param un_ambiguous_date: an un-ambiguous scrambled date,
of the format X/Y/Z where X, Y, Z correspond to
day, month, year but not necessarily in this order.
@return: The date in the order 'DAY/MONTH/YEAR'.
A date is un-ambiguous when each number may be
decided to be day or month or year given the range
limitation of this numbers.
(For example 14 cannot be a month number, and
2008 cannot be a day number)
>>> order_date('3/2015/13')
'13/3/2015'
>>> order_date('2012/20/4')
'20/4/2012'
# Here both 3/4/1000 and 4/3/1000 are possible.
>>> order_date('3/4/1000')
Traceback (most recent call last):
...
ValueError: Ambiguous date was given
>>> order_date('3/3/2050')
'3/3/2050'
>>> order_date('1/1/1')
Traceback (most recent call last):
...
ValueError: The date cannot be valid
>>> order_date('12/6')
Traceback (most recent call last):
...
ValueError: The date is too short, format should be X/Y/Z
>>> order_date('2000/2000')
Traceback (most recent call last):
...
ValueError: The date is too short, format should be X/Y/Z
>>> order_date('Foo/Bar/Baz')
Traceback (most recent call last):
...
TypeError: The date should be made up of '/' separated INTEGERS
"""
decided to be day or month or year given the range
limitation of these numbers.
For example
- \$14\$ cannot be a month number
- \$2008\$ cannot be a day number
@param
un_ambiguous_date: an un-ambiguous scrambled date,of the format X/Y/Z where X, Y, Z correspond to
day, month, year but not necessarily in this order.
@return The date in the order 'DAY/MONTH/YEAR'.
I am particularly interested in full input validation and nice error producing, so tell me any lack of clarity/corner case missed.
For example usage see the doctests included with the function:
```
def order_date(un_ambiguous_date):
"""
@param un_ambiguous_date: an un-ambiguous scrambled date,
of the format X/Y/Z where X, Y, Z correspond to
day, month, year but not necessarily in this order.
@return: The date in the order 'DAY/MONTH/YEAR'.
A date is un-ambiguous when each number may be
decided to be day or month or year given the range
limitation of this numbers.
(For example 14 cannot be a month number, and
2008 cannot be a day number)
>>> order_date('3/2015/13')
'13/3/2015'
>>> order_date('2012/20/4')
'20/4/2012'
# Here both 3/4/1000 and 4/3/1000 are possible.
>>> order_date('3/4/1000')
Traceback (most recent call last):
...
ValueError: Ambiguous date was given
>>> order_date('3/3/2050')
'3/3/2050'
>>> order_date('1/1/1')
Traceback (most recent call last):
...
ValueError: The date cannot be valid
>>> order_date('12/6')
Traceback (most recent call last):
...
ValueError: The date is too short, format should be X/Y/Z
>>> order_date('2000/2000')
Traceback (most recent call last):
...
ValueError: The date is too short, format should be X/Y/Z
>>> order_date('Foo/Bar/Baz')
Traceback (most recent call last):
...
TypeError: The date should be made up of '/' separated INTEGERS
"""
Solution
Wrong bounds
Your filters on your day/month aren't right:
31 is a valid day and 12 is a valid month, so those should be
Dealing with the tuple
Since just about everywhere you deal with
The last join
Instead of:
I would just use format:
It's shorter and more readable.
Combine the last two checks
I would write a helper function that returns the first element of a single-element list:
And then you can just write:
Your filters on your day/month aren't right:
day = [i for i in (x,y,z) if i < 31]
month = list(set(i for i in (x,y,z) if i < 12))31 is a valid day and 12 is a valid month, so those should be
<=. Additionally, you're not excluding negative numbers - so you'll want 1 <= i <= 31 for the first filter and 1 <= i <= 12 for the second.Dealing with the tuple
Since just about everywhere you deal with
(x,y,z) as a tuple, I would just keep it as a tuple the whole way and not unpack it. You only unpack it to check the length, so we can just make that check explicit. Also, if I passed in something like 1/2/3/4/5, you'd tell me that my date was too short, so this can get both right:elems = un_ambiguous_date.split('/')
if len(elems) != 3:
raise ValueError("The date is too {}, format should be X/Y/Z".format(
'short' if len(elems) < 3 else 'long'))
try:
elems = map(int, elems)
except ValueError:
raise ...
day = [i for i in elems if 1 <= i <= 31]
# etc.The last join
Instead of:
return '/'.join(map(str, (day[0], month[0], year[0])))I would just use format:
return '{}/{}/{}'.format(day[0], month[0], year[0])It's shorter and more readable.
Combine the last two checks
I would write a helper function that returns the first element of a single-element list:
def front(lst):
if not lst:
raise ValueError("The date cannot be valid")
if len(lst) > 1:
raise ValueError("Ambiguous date was given")
return lst[0]And then you can just write:
return '{}/{}/{}'.format(front(day), front(month), front(year))Code Snippets
day = [i for i in (x,y,z) if i < 31]
month = list(set(i for i in (x,y,z) if i < 12))elems = un_ambiguous_date.split('/')
if len(elems) != 3:
raise ValueError("The date is too {}, format should be X/Y/Z".format(
'short' if len(elems) < 3 else 'long'))
try:
elems = map(int, elems)
except ValueError:
raise ...
day = [i for i in elems if 1 <= i <= 31]
# etc.return '/'.join(map(str, (day[0], month[0], year[0])))return '{}/{}/{}'.format(day[0], month[0], year[0])def front(lst):
if not lst:
raise ValueError("The date cannot be valid")
if len(lst) > 1:
raise ValueError("Ambiguous date was given")
return lst[0]Context
StackExchange Code Review Q#114476, answer score: 6
Revisions (0)
No revisions yet.