Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 757
wolfgang Avatar asked Jul 24 '15 12:07

wolfgang


1 Answers

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), [])
like image 94
Holt Avatar answered Oct 17 '22 22:10

Holt