Vereenvoudig een datumvergelijking

Ik heb de volgende python-code die een datumvergelijking doet voor een lijstsorteerfunctie (bijv. date_list.sort (cmp = dcmp) . Ik vraag me af of het mogelijk is om de cmp/if-test te verwijderen en maak er een oneliner van.

def dcmp(a, b):
    amm, add, ayy = [int(v) for v in a.split('/')]
    bmm, bdd, byy = [int(v) for v in b.split('/')]
    v = cmp(ayy, byy)
    if v != 0: return v
    v = cmp(amm, bmm)
    if v != 0: return v
    return cmp(add, bdd)

Update: Laat me duidelijk maken waar ik nieuwsgierig naar ben, kun je dit blok code uit de functie verwijderen:

    v = cmp(ayy, byy)
    if v != 0: return v
    v = cmp(amm, bmm)
    if v != 0: return v
    return cmp(add, bdd)
1
Omdat ze worden gelezen uit een bestand en eruitzien als '10/12/2011 'en worden gebruikt als dictaattoetsen.
toegevoegd de auteur koblas, de bron
Waarom zijn de argumentreeksen om mee te beginnen? Python heeft een datum type.
toegevoegd de auteur NullUserException, de bron
U kunt ook datum -objecten als sleutels gebruiken.
toegevoegd de auteur NullUserException, de bron
Ik denk nog steeds dat NullUserException een punt heeft. U kunt die strings eenvoudig omzetten in datetime-objecten met behulp van strptime . Hiermee kunt u vergelijkingen maken en kunt u ze nog steeds gebruiken als dictaattoetsen.
toegevoegd de auteur aganders3, de bron

5 antwoord

U kunt de -code gebruiken in plaats van cmp . Het is een stuk eenvoudiger om een ​​ sleutel -functie te schrijven, en kan op één regel worden gedaan.

date_list.sort(key = lambda s: datetime.strptime(s, '%m/%d/%Y'))

U zult deze import ook nodig hebben:

from datetime import datetime
2
toegevoegd
Om nog maar te zwijgen over het feit dat het gebruik van sleutel waarschijnlijk efficiënter zal zijn.
toegevoegd de auteur PaulMcG, de bron

Vergeet de datumconversies, u kunt dit als strings doen. Zet ze gewoon in de juiste volgorde.

amm, add, ayy = a.split('/')
bmm, bdd, byy = b.split('/')
return cmp(ayy+amm+add, byy+bmm+bdd)

Ik gebruik liever de ISO-datumindeling , waarmee de dingen in de juiste volgorde worden geplaatst: YYYY-MM-DD . Het heeft ook het voordeel dat het niet verkeerd geïnterpreteerd wordt aan de andere kant van de oceaan.

1
toegevoegd
@JohnMachin, eerlijke kritiek. Zoals ik al zei, geef ik er de voorkeur aan om formaten te gebruiken waar de voorloopnullen gegarandeerd zijn. En bedankt voor het uitleggen van het downvote, ik waardeer het veel beter dan de anonieme soort.
toegevoegd de auteur Mark Ransom, de bron
-1 In tegenstelling tot de OP-code, zal dit FAIL zijn als de dagen of maanden geen voorloopnullen hebben.
toegevoegd de auteur John Machin, de bron
Zo waar aan de andere kant van de oceaan. Klassiek falen: een batchtaak verwerpt ongeveer 60% van de geboortedata en de begindatum van het dienstverband in een bestand dat wordt aangeleverd door een nieuwe "andere kant" -dochter. Verwerpt moeizaam "gecorrigeerd" en opnieuw ingevoerd. Geen realisatie (tot veel later) dat een andere ongeveer 37% zou zijn gevuld, maar niet gedetecteerd.
toegevoegd de auteur John Machin, de bron

Het was duidelijk dat ik alleen deze code wilde vervangen ( negeer dat het datums waren )

v = cmp(ayy, byy)
if v != 0: return v
v = cmp(amm, bmm)
if v != 0: return v
return cmp(add, bdd)

Het juiste antwoord is:

return cmp(ayy, byy) or cmp(amm, bmm) or cmp(add, bdd)

Die kan ook worden geschreven als:

return cmp((ayy,amm,add), (byy,bmm,bdd))
0
toegevoegd

Als al je maanden en dagen nulgevuld zijn en je datums in m/d/Y-formaat zijn zoals "01/01/2001", kun je schrijven:

def datekey(d):
    m, d, y = d.split('/')
    return (y, m, d)

datelist.sort(key=datekey)

Als de getallen niet altijd met nuldeks zijn gevuld, kunt u elk nummer voor vergelijkingsdoelen naar een int casten:

def datekey(d):
    m, d, y = d.split('/')
    return (int(y), int(m), int(d))

datelist.sort(key=datekey)

Dit is manier sneller dan strptime gebruiken als al je datums in zo'n eenvoudige indeling zijn:

>>> import timeit
>>> print timeit.Timer("datelist.sort(key=datekey)",
...     setup="""\
... datelist = ['01/01/2001', '01/02/2001', '12/31/1999']
... def datekey(d):
...     m, d, y = d.split('/')
...     return (y, m, d)
... """).timeit()
3.3154168129

>>> print timeit.Timer("datelist.sort(key=datekey)",
...     setup="""\
... datelist = ['01/01/2001', '01/02/2001', '12/31/1999']
... def datekey(d):
...     m, d, y = d.split('/')
...     return (int(y), int(m), int(d))
... """).timeit()
11.1701700687

>>> print timeit.Timer("datelist.sort(key = lambda s: datetime.datetime.strptime(s, '%m/%d/%Y'))",
...     setup="""\
... datelist = ['01/01/2001', '01/02/2001', '12/31/1999']
... import datetime
... """).timeit()
59.2817358971

strptime is hugely powerful, very important to know, and very slow in this use case.

0
toegevoegd
-1 Benchmarks voor onjuiste code zijn zinloos. datekey ('12/31/1999 ') produceert [1999, 31, 12] FAIL
toegevoegd de auteur John Machin, de bron
Aww, crud. Bewerkt. Het heeft geen zin om een ​​eikel te zijn over de FAIL.
toegevoegd de auteur Kirk Strauser, de bron

Het is zeker schoner om deze waarden naar echte datums te converteren (hoewel misschien langzamer - je moet het benchmarken als snelheid zo belangrijk is):

def dcmp(a, b):
    from datetime.datetime import strptime
    return strptime(a,'%m/%d/%Y') > strptime(b,'%m/%d/%Y')
0
toegevoegd