wolfgang
wolfgang

Reputation: 7819

python algorithm to be done in a pythonic fashion?

Given an input text array

input_array = [
    'JUNK', 'Mon', 'JUNK', '10am', 'JUNK', '-', ' 5pm',
    '6pm', '-', '9pm', 'JUNK', 'Tue', '10am', '-', 'JUNK', '5pm'
]

should be converted to a JSON

[
  {
    "weekday_name": "monday",
    "starting_time": "10am",
    "ending_time": "5pm"
  },
  {
    "weekday_name": "monday",
    "starting_time": "6pm",
    "ending_time": "10pm"
  },
  ...
]

Although this is a simple algorithm, I'm forced to create temporary variables which is usually considered non-pythonic.

The code with ugly temporary variables

import pprint

input_array = ['JUNK','Mon','JUNK','10am','JUNK','-','5pm','6pm','-','9pm','JUNK','Tue','10am','-','JUNK','5pm']

business_hours = []
start_hours = None
end_hours = None
current_day = None
dash_found = False

days_of_the_week = {}
days_of_the_week['Mon'] = 'monday'
days_of_the_week['Tue'] = 'tuesday'
days_of_the_week['Wed'] = 'wednesday'
days_of_the_week['Thu'] = 'thursday'
days_of_the_week['Fri'] = 'friday'
days_of_the_week['Sat'] = 'saturday'
days_of_the_week['Sun'] = 'sunday'

for x in input_array:
    if x in days_of_the_week:
        current_day = days_of_the_week[x]     
    elif x[0].isdigit() and dash_found == False:
        starting_time = x        
    elif x == '-':
        dash_found = True    
    elif x[0].isdigit() and dash_found == True:
        ending_time = x
        business_hours.append({"weekday_name":current_day,"starting_time":starting_time,"ending_time":ending_time})
        dash_found = False


pprint.pprint(business_hours)

Can i make my code less ugly and accomplish the same without having to create many temporary variables in python?

Upvotes: 5

Views: 151

Answers (2)

Holt
Holt

Reputation: 37706

Since your data are always in the same order:

# Here you remove all the cells in your array which are not a day of the week
# or a time (not the call to "list" before filter object are not indexable in Python 3+)
data = list(filter(lambda x: x in days_of_the_week or x[0].isdigit(), input_array))
while data:
    # Get the first item available and remove it
    curday = days_of_the_week[data.pop(0)]
    # For each couple (start, end), add an item to business_hours
    while data and data[0][0].isdigit():
        business_hours.append({
            'weekday_name':  curday,
            'starting_time': data[0],
            'ending_time':   data[1]
        })
        data = data[2:]

There are certainly plenty of way of doing this, you will probably be stuck with the curday variable since you have to memorize it (you could use the latest entry in business_hours but it would be uglier, IMO).

Edit: Someone suggested an edit saying list is not necessary. Since I don't know which version of python you are using, I assume python 3.0, in which case list is mandatory (filter are kind of generator in python 3.0, so they cannot be indexed). If you are using python 2.0, list may not be mandatory.


For the fun of it, here is a one liner using reduce (do I have to tell you not to do that?):

from functools import reduce # Okay, okay, 2 lines!
reduce (lambda r, x: r + [{'weekday_name': days_of_the_week[x]}] if x in days_of_the_week 
                     else r + [{'weekday_name': r[-1]['weekday_name'], 'starting_time': x}] if len(r[-1]) == 3 
                     else (r[-1].update({'starting_time': x}), r)[1] if len(r[-1]) == 1 
                     else (r[-1].update({'ending_time': x}), r)[1], 
        filter(lambda x: x in days_of_the_week or x[0].isdigit(), input_array), [])

Upvotes: 6

Martin Evans
Martin Evans

Reputation: 46789

The following script is fairly forgiving in terms of the input, as such I cannot see a way around a couple of variables.

I have added another test case for Sunday with 3 opening times and some differently placed JUNK.

import pprint

input_array = [
    'JUNK', 'Mon', 'JUNK', '10am', 'JUNK', '-', ' 5pm',
    '6pm', '-', '9pm', 'JUNK', 'Tue', '10am', '-', 'JUNK', '5pm',
    "Sun  ", " 8am", "-  ", "JUNK", "MORE JUNK", " 9pm", "10am  ", "-", " 1pm", " 6pm", "-", "8pm"]

days_of_the_week = {'mon' : 'monday', 'tue' : 'tuesday', 'wed' : 'wednesday',
    'thu' : 'thursday', 'fri' : 'friday', 'sat' : 'saturday', 'sun' : 'sunday'}

business_hours = []
start = None

for entry in input_array:
    entry = entry.strip().lower()
    if entry in days_of_the_week:
        current_day = days_of_the_week[entry]
    elif entry[0].isdigit() and start:
        business_hours.append({"weekday_name": current_day, "starting_time": start, "ending_time": entry})
        start = None
    elif entry[0].isdigit():
        start = entry

pprint.pprint(business_hours)

Giving the following output:

[{'ending_time': '5pm', 'starting_time': '10am', 'weekday_name': 'monday'},
 {'ending_time': '9pm', 'starting_time': '6pm', 'weekday_name': 'monday'},
 {'ending_time': '5pm', 'starting_time': '10am', 'weekday_name': 'tuesday'},
 {'ending_time': '9pm', 'starting_time': '8am', 'weekday_name': 'sunday'},
 {'ending_time': '1pm', 'starting_time': '10am', 'weekday_name': 'sunday'},
 {'ending_time': '8pm', 'starting_time': '6pm', 'weekday_name': 'sunday'}]

Upvotes: 0

Related Questions