2010-12-19 16 views
6

Tengo una secuencia del día de la semana. código Python de lo que quiero hacer:Python: Convertir ('Lunes', 'Martes', 'Miércoles') a 'Lunes a Miércoles'

def week_days_to_string(week_days): 
    """ 
    >>> week_days_to_string(('Sunday', 'Monday', 'Tuesday')) 
    'Sunday to Tuesday' 
    >>> week_days_to_string(('Monday', 'Wednesday')) 
    'Monday and Wednesday' 
    >>> week_days_to_string(('Sunday', 'Wednesday', 'Thursday')) 
    'Sunday, Wednesday, Thursday' 
    """ 
    if len(week_days) == 2: 
     return '%s and %s' % weekdays 
    elif week_days_consecutive(week_days): 
     return '%s to %s' % (week_days[0], week_days[-1]) 
    return ', '.join(week_days) 

sólo necesito la función week_days_consecutive (la parte dura je).

Alguna idea de cómo podría lograrlo?

Aclaración:

Mi redacción y ejemplos causado cierta confusión. No solo quiero limitar esta función a la semana laboral. Quiero considerar todos los días de la semana (S, M, T, W, T, F). Mis disculpas por no haber sido claro sobre eso anoche. Editado el cuerpo de la pregunta para hacerlo más claro.

Editar: Lanzar algunas llaves en él

secuencia del cruzado:

>>> week_days_to_string(('Sunday', 'Monday', 'Tuesday', 'Saturday')) 
'Saturday to Tuesday' 

Y, por @ user470379 y opcional:

>>> week_days_to_string(('Monday, 'Wednesday', 'Thursday', 'Friday')) 
'Monday, Wednesday to Friday' 
+2

¿Debería ('lunes', 'miércoles', 'jueves', 'viernes') volver 'lunes, miércoles, jueves, viernes' o 'lunes, miércoles a viernes'? – user470379

+0

Eso sería genial también. –

+0

Mientras esté sin llaves, creo que puedo tener una respuesta que cumpla con la pregunta. –

Respuesta

5

Me gustaría abordar este problema por:

  • Creación de una correlación dict nombres de los días a su índice secuencial
  • conversión de mis nombres de los días de entrada a sus índices secuenciales
  • Mirando a los índices de entrada resultantes y pidiendo si son secuenciales

Así es como se puede hacer eso, usando calendar.day_name, range y algunos de comprensiones:

day_indexes = {name:i for i, name in enumerate(calendar.day_name)} 
def weekdays_consecutive(days): 
    indexes = [day_indexes[d] for d in days] 
    expected = range(indexes[0], indexes[-1] + 1) 
    return indexes == expected 

algunas otras opciones, dependiendo de lo que necesita:

  • Si necesita Python < 2.7, en lugar de la comprensión dict, puede utilizar:

    day_indexes = dict((name, i) for i, name in enumerate(calendar.day_name)) 
    
  • Si no desea permitir el sábado y el domingo, simplemente recorte los últimos dos días:

    day_indexes = ... calendar.day_name[:-2] ... 
    
  • Si necesita envolver alrededor después del domingo, es probable que sea más fácil de comprobar sólo que cada artículo es uno más que el punto anterior, pero trabajando en módulo 7:

    def weekdays_consecutive(days): 
        indexes = [day_indexes[d] for d in days] 
        return all(indexes[i + 1] % 7 == (indexes[i] + 1) % 7 
           for i in range(len(indexes) - 1)) 
    

Actualización: para el problema extendido, aun así seguir con dict que el día a índice, pero en su lugar lo haría:

  • Encuentra todos los índices en un plazo de días secuenciales detiene
  • abrigo de los días en torno a si es necesario para obtener la secuencia más larga posible de días
  • Grupo los días en sus tramos secuenciales

Aquí está el código para hacer esto:

def weekdays_to_string(days): 
    # convert days to indexes 
    day_indexes = {name:i for i, name in enumerate(calendar.day_name)} 
    indexes = [day_indexes[d] for d in days] 

    # find the places where sequential days end 
    ends = [i + 1 
      for i in range(len(indexes)) 
      if (indexes[(i + 1) % len(indexes)]) % 7 != 
       (indexes[(i) % len(indexes)] + 1) % 7] 

    # wrap the days if necessary to get longest possible sequences 
    split = ends[-1] 
    if split != len(days): 
     days = days[split:] + days[:split] 
     ends = [len(days) - split + end for end in ends] 

    # group the days in sequential spans 
    spans = [days[begin:end] for begin, end in zip([0] + ends, ends)] 

    # format as requested, with "to", "and", commas, etc. 
    words = [] 
    for span in spans: 
     if len(span) < 3: 
      words.extend(span) 
     else: 
      words.append("%s to %s" % (span[0], span[-1])) 
    if len(days) == 1: 
     return words[0] 
    elif len(days) == 2: 
     return "%s and %s" % tuple(words) 
    else: 
     return ", ".join(words) 

También podrían tratar el siguiente en lugar de ese último bloque if/elif/else para obtener un "y" entre los dos últimos elementos y comas entre todo lo demás:

if len(words) == 1: 
     return words[0] 
    else: 
     return "%s and %s" % (", ".join(words[:-1]), words[-1]) 

Eso es un poco diferente de la especificación, pero más bonito en mis ojos.

+0

+1 para usar el calendario, y que todos funcionan. No conocía a ninguno de ellos antes de –

+0

Muy bien. Y me gusta especialmente el fragmento '% s y% s'. Son las cosas pequeñas, ¿eh? –

0

que tendría que comprobar la primera fecha dado, luego tenga una lista con todos los días de la semana, verifique si el próximo día está en el siguiente índice de la lista, y repita.

Esto se puede hacer fácilmente con algunos bucles, suponiendo que los días están en orden.

0

Esto tomaría una intrincada lógica caso por caso o un almacenamiento codificado de todos los días de forma secuencial. Prefiero lo último.

def weekdays_consecutive(x): 
    allDays = { 'Monday':1, 'Tuesday':2, 'Wednesday':3, 'Thursday':4, 'Friday':5, 'Saturday' : 6, 'Sunday' : 7} 
    mostRecent = x[0] 
    for i in x[1:]: 
     if allDays[i] % 7 != allDays[mostRecent] % 7 + 1: return False 
     mostRecent = i 
    return True 

Y esto puede ordenar la entrada: x.sort(lambda x, y: allDays[x] - allDays[y]). No sé qué función prefiere usar en

>>>x = ['Tuesday', 'Thursday', 'Monday', 'Friday'] 
>>>x.sort(lambda x, y: allDays[x] - allDays[y]) 
>>>x 
['Monday', 'Tuesday', 'Thursday', 'Friday'] 

Esto no se basa en la presencia de no días. Me imagino que querría lidiar con esto en la función weekdays_to_string en lugar de aquí en weekdays_consecutive.

También creo que desea cambiar el primer caso de su otra función a 'y' en lugar de 'a' y agregar mayúsculas y minúsculas para las entradas de un día.

EDIT: tuve un error bastante tonto que acabo de arreglar, debería funcionar ahora!

-1
import itertools 

#probably a better way to obtain this like with the datetime library 
WEEKDAYS = (('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')) 

def weekdays_consecutive(days): 
    #assumes that days only contains valid weekdays 

    if len(days) == 0: 
     return True #or False? 
    iter = itertools.cycle(WEEKDAYS) 
    while iter.next() != days[0]: pass 
    for day in days[1:]: 
     if day != iter.next(): return False 
    return True 

#... 

>>> weekdays_consecutive(('Friday', 'Monday')) 
True 
>>> weekdays_consecutive(('Friday', 'Monday', 'Tuesday')) 
True 
>>> weekdays_consecutive(('Friday', 'Monday', 'Tuesday', 'Thursday')) 
False 
+0

No obtuve el mismo comportamiento que en el último ejemplo. Estoy en apuros para ver cómo se supone que debe hacer algo además de verificar que todos los elementos de entrada estén en WEEKDAY_SET. Nada que ver con ser consecutivo. Tal vez en estos casos de prueba todavía estaba ejecutando la función de su ejemplo anterior? –

+0

@jon_darkstar Y es por eso que generalmente no escribo el código a altas horas de la noche>. <. Tienes razón, ese código era totalmente falso. – user470379

+0

= P los otros eran buenos –

0

No he probado Debo decirlo.

def test(days): 
    days = list(days) 
    if len(days) == 1: 
    return days[0] 
    elif len(days) == 2: 
    return ' to '.join(days) 
    else: 
    return ''.join(days[:1] + [' to ' + days[-1]]) 
2
def weekdays_consecutive(inp): 
    days = { 'Monday': 0, 
      'Tuesday': 1, 
      'Wednesday': 2, 
      'Thursday': 3, 
      'Friday': 4 } 

    return [days[x] for x in inp] == range(days[inp[0]], days[inp[-1]] + 1) 

Como ya se ha comprobado en los demás casos, creo que esto va a ser lo suficientemente bueno.

1

Aquí está mi solución completa, puede usarla como quiera; (el código se está poniendo en el dominio público, pero no aceptaré ninguna responsabilidad si algo le sucede a usted o a su computadora como consecuencia de su uso y no hay garantía yadda yadda ya).

week_days = { 
    'monday':0, 
    'tuesday':1, 
    'wednesday':2, 
    'thursday':3, 
    'friday':4, 
    'saturday':5, 
    'sunday':6 
} 
week_days_reverse = dict(zip(week_days.values(), week_days.keys())) 

def days_str_to_int(days): 
    ''' 
    Converts a list of days into a list of day numbers. 
    It is case ignorant. 
    ['Monday', 'tuesday'] -> [0, 1] 
    ''' 
    return map(lambda day: week_days[day.lower()], days) 

def day_int_to_str(day): 
    ''' 
    Converts a day number into a string. 
    0 -> 'Monday' etc 
    ''' 
    return week_days_reverse[day].capitalize() 

def consecutive(days): 
    ''' 
    Returns the number of consecutive days after the first given a sequence of 
    day numbers. 
    [0, 1, 2, 5] -> 2 
    [6, 0, 1] -> 2 
    ''' 
    j = days[0] 
    n = 0 
    for i in days[1:]: 
     j = (j + 1) % 7 
     if j != i: 
      break 
     n += 1 
    return n 

def days_to_ranges(days): 
    ''' 
    Turns a sequence of day numbers into a list of ranges. 
    The days can be in any order 
    (n, m) means n to m 
    (n,) means just n 
    [0, 1, 2] -> [(0, 2)] 
    [0, 1, 2, 4, 6] -> [(0, 2), (4,), (6,)] 
    ''' 
    days = sorted(set(days)) 
    while days: 
     n = consecutive(days) 
     if n == 0: 
      yield (days[0],) 
     else: 
      assert n < 7 
      yield days[0], days[n] 
     days = days[n+1:] 

def wrap_ranges(ranges): 
    ''' 
    Given a list of ranges in sequential order, this function will modify it in 
    place if the first and last range can be put together. 
    [(0, 3), (4,), (6,)] -> [(6, 3), (4,)] 
    ''' 
    if len(ranges) > 1: 
     if ranges[0][0] == 0 and ranges[-1][-1] == 6: 
      ranges[0] = ranges[-1][0], ranges[0][-1] 
      del ranges[-1] 

def range_to_str(r): 
    ''' 
    Converts a single range into a string. 
    (0, 2) -> "Monday to Wednesday" 
    ''' 
    if len(r) == 1: 
     return day_int_to_str(r[0]) 
    if r[1] == (r[0] + 1) % 7: 
     return day_int_to_str(r[0]) + ', ' + day_int_to_str(r[1]) 
    return day_int_to_str(r[0]) + ' to ' + day_int_to_str(r[1]) 

def ranges_to_str(ranges): 
    ''' 
    Converts a list of ranges into a string. 
    [(0, 2), (4, 5)] -> "Monday to Wednesday, Friday, Saturday" 
    ''' 
    if len(ranges) == 1 and ranges[0] == (0, 6): 
     return 'all week' 
    return ', '.join(map(range_to_str, ranges)) 

def week_days_to_string(days): 
    ''' 
    Converts a list of days in string form to a stringed list of ranges. 
    ['saturday', 'monday', 'wednesday', 'friday', 'sunday'] -> 
     'Friday to Monday, Wednesday' 
    ''' 
    ranges = list(days_to_ranges(days_str_to_int(days))) 
    wrap_ranges(ranges) 
    return ranges_to_str(ranges) 

Características:

  • Soporta más de un rango,
  • Se pueden introducir en los días en cualquier orden,
  • Será envolver,

Agregue comentarios si encuentra algún problema y haré todo lo posible para solucionarlos.

Cuestiones relacionadas