7.5 DRILL — Ledd 3b: rangér og sortér tabellen
Rangere hele tabellen på en kolonne — boblesortering, sorted med key, eller en utlevert sort_list — og lage topplister.
- Sortering står i ~70 % av settene (7 av 10 sittinger i arkivet 2014–2019) og er verdt 5–10 % når den er der. Den hører til sjanger G — analyse, som resten av kap. 7.4.
- Bestillingen er nesten alltid den samme: «rangér butikkene etter pris», «lag en toppliste over de tre beste», «sortér tabellen på kolonnen brukeren velger».
- Noen sett leverer sorteringsfunksjonen ferdig i oppgaveteksten, ofte kalt sort_list. Da er poenget å vise at du kaller den riktig, ikke å skrive sorteringen på nytt. Les alltid om den er der.
- Den ene fellen som ødelegger hele deloppgaven, er å sortere kolonnen i stedet for radene. Da mister du koblingen mellom navnet og tallet, og svaret blir tilfeldig.
Prioritet: må kunne — det midterste av bokas tre nivåer, som er «må sitte», «må kunne» og «bør kjenne til». Sorteringen er ikke like sikker som innlesingen og analysen, men den kommer ofte nok til å være verdt en kveld.
Kapitlet tar ~60 minutter og er delt i fem løkker med tidsanslag.
Sorteringsverktøyet er gjennomgått i kap. 6.1; her brukes det på tabeller fra kjeden. Tre grep, ferdig oppfrisket:
1. sorted gir ei ny liste, .sort() endrer den gamle:
tall = [1290, 990, 1450]
print(sorted(tall))
print(tall)Utskrift:
[990, 1290, 1450]
[1290, 990, 1450]2. key bestemmer hva som sammenlignes:
rader = [['Bjornhytta', 1290], ['Steinbua', 760]]
print(sorted(rader, key=lambda rad: rad[1]))Utskrift:
[['Steinbua', 760], ['Bjornhytta', 1290]]3. Boblesorteringen bytter naboer (kap. 6.1):
x = [3, 1, 2]
for i in range(len(x) - 1):
for j in range(len(x) - 1 - i):
if x[j] > x[j + 1]:
x[j], x[j + 1] = x[j + 1], x[j]
print(x)Utskrift:
[1, 2, 3]I tillegg forutsetter kapitlet kap. 7.4 — analysen tabellen kommer fra — og kap. 3.2, som ga oss tabell[0].index(navn).
Løkke 1 — rangér radene, ikke kolonnen (~13 min)
Den viktigste innsikten i hele kapitlet kommer først: du sorterer alltid rader. En rad er en enhet der navnet, kommunen, prisen og sengetallet hører sammen. Trekker du ut priskolonnen og sorterer den alene, har du kastet den koblingen for godt.
Å rangere er å sette radene i rekkefølge etter én kolonne, og som regel vise de beste først.
Tre formuleringer du møter, med hver sin nyanse:
- «Sortér tabellen på pris» — hele tabellen i ny rekkefølge.
- «Lag en toppliste over de tre billigste» — sorter, og ta utsnittet [:3].
- «Returnér navnene på de tre billigste» — sorter, ta utsnittet, og plukk ut navnekolonnen til slutt.
Rekkefølgen på arbeidet er den samme i alle tre. Forskjellen er bare hvor langt du går før du returnerer, og det er verdt å lese oppgaveteksten to ganger for å treffe riktig.
Sammenlign de to:
rader = [['Bjornhytta', 1290], ['Steinbua', 760], ['Fugleredet', 1450]]
priser = [rad[1] for rad in rader]
print(sorted(priser))
print(sorted(rader, key=lambda rad: rad[1]))Utskrift:
[760, 1290, 1450]
[['Steinbua', 760], ['Bjornhytta', 1290], ['Fugleredet', 1450]]Den første linja gir prisene i rekkefølge, men ingen vet lenger hvilken hytte som koster hva. Den er ubrukelig som svar på «hvilken hytte er billigst».
Den andre flytter hele rader, så alle fire feltene følger med. Det er den formen alle sorteringsoppgaver i dette faget vil ha.
Unntaket er når oppgaven uttrykkelig ber om bare tallene: «returnér prisene sortert stigende». Da er kolonneuttrekket riktig — men les setningen én gang til før du stoler på det.
Fire steg, alltid i denne rekkefølgen:
k = tabell[0].index('pris')
rader = tabell[1:]
sortert = sorted(rader, key=lambda rad: rad[k])
return sortertBlokken over er mønsteret; den ferdige funksjonen står i eksempel 1.
Hvorfor hvert steg er der:
1. Kolonnen ved navn — samme fullscore-krav som ellers i kjeden. Kommer kolonnene i en annen rekkefølge, virker koden likevel.
2. Skill ut overskriftsraden — den skal ikke sorteres. 'pris' som tekst ville havnet et vilkårlig sted blant tallene, eller gitt TypeError.
3. Sortér med key — eller med boblesortering, eller med en utlevert sort_list. Alle tre er riktige.
4. Returnér — enten bare radene, eller overskriften limt på igjen med [tabell[0]] + sortert. Oppgaveteksten sier hva som skal med.
Gir ei ny liste med de samme radene i ny rekkefølge. Den opprinnelige lista er urørt, og radene selv er ikke kopiert — de er de samme listene, bare i en annen rekkefølge.
rader = [['Bjornhytta', 1290], ['Steinbua', 760]]
sortert = sorted(rader, key=lambda rad: rad[1])
print(sortert)
print(rader)Utskrift:
[['Steinbua', 760], ['Bjornhytta', 1290]]
[['Bjornhytta', 1290], ['Steinbua', 760]]k må være et tall når lambdaen kjøres, så finn kolonnenummeret først og bruk variabelen. Skriver du key=lambda rad: rad['pris'], får du TypeError — en liste indekseres med tall, ikke med navn.
Formen rader.sort(key=lambda rad: rad[k]) gjør det samme, men endrer lista i stedet for å lage en ny. Begge er riktige; sorted er tryggest når den som kalte deg fortsatt trenger den gamle rekkefølgen.
sorted gir stigende rekkefølge: minst først. Det er riktig for pris, tid og feilrate, og galt for poeng, salg og omsetning.To ord i oppgaveteksten avgjør:
- «billigste», «raskeste», «minst» betyr stigende, altså sorted(...) slik den er.
- «dyreste», «beste», «flest», «mest» betyr synkende, altså reverse=True.
Det er verdt å skrive svaret ditt ut med to–tre rader og se på det. Står 760 øverst når oppgaven ba om den dyreste, ser du feilen på ett sekund — og det er en av de billigste kontrollene på hele eksamen.
fjellstuer.txt har overskriftsraden navn;kommune;pris;senger. Skriv rangert_etter(tabell, kolonnenavn) som returnerer radene sortert stigende på den kolonnen, uten overskriftsraden.»Slik ser fila ut:
Innholdet i fjellstuer.txt:
navn;kommune;pris;senger
Bjornhytta;Vang;1290;18
Skarvheim;Hemsedal;990;24
Fugleredet;Vang;1450;12
Snofonna;Hemsedal;990;40
Steinbua;Lom;760;8def rangert_etter(tabell, kolonnenavn):
k = tabell[0].index(kolonnenavn)
return sorted(tabell[1:], key=lambda rad: rad[k])
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
for rad in rangert_etter(tabell, 'pris'):
print(rad)Utskrift:
['Steinbua', 'Lom', 760, 8]
['Skarvheim', 'Hemsedal', 990, 24]
['Snofonna', 'Hemsedal', 990, 40]
['Bjornhytta', 'Vang', 1290, 18]
['Fugleredet', 'Vang', 1450, 12]Legg merke til Skarvheim og Snøfonna: begge koster 990, og de står i den samme innbyrdes rekkefølgen som i tabellen. Python-sortering er stabil — like elementer beholder rekkefølgen de hadde.
Den samme funksjonen på en annen kolonne, uten en eneste endring:
def rangert_etter(tabell, kolonnenavn):
k = tabell[0].index(kolonnenavn)
return sorted(tabell[1:], key=lambda rad: rad[k])
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
for rad in rangert_etter(tabell, 'senger'):
print(rad[0], rad[3])Utskrift:
Steinbua 8
Fugleredet 12
Bjornhytta 18
Skarvheim 24
Snofonna 40Slik ville sensor sett på det. Deloppgaven er typisk 10 %, og fire ting gir uttelling hver for seg: kolonnenavnet som parameter, oppslaget med .index(...), tabell[1:] som holder overskriften utenfor, og at hele rader flyttes.
At funksjonen tar kolonnenavnet som parameter er det som gjør den gjenbrukbar — og en senere deloppgave om en meny der brukeren velger sorteringskolonne, blir da nesten gratis.
Flere korrekte løsninger: boblesortering på rader gir samme svar og full pott, og kommer i løkke 3. Er sort_list utlevert i oppgaveteksten, er det den du skal kalle.
Kantfeilen: rangert_etter(tabell, 'Pris') med stor forbokstav gir ValueError: 'Pris' is not in list. Vil du være snill mot brukeren, sammenligner du med .lower() — men det er ikke krevd her.
(Innstegsoppgave, sjanger B — kodesporing, altså «hva skriver dette programmet ut?».) Hva skriver programmet ut? Skriv begge linjene.
priser = [1290, 990, 1450]
resultat = priser.sort()
print(resultat)
print(priser)rangert_med_overskrift(tabell, kolonnenavn) som returnerer tabellen sortert stigende på den oppgitte kolonnen, med overskriftsraden fortsatt som rad 0.»Test på kolonnen senger.
Løkke 2 — retning og toppliste (~12 min)
Når rekkefølgen er på plass, er resten to små grep: snu retningen, og ta et utsnitt.
Snur rekkefølgen sorteringen gir. Virker sammen med key.
rader = [['Bjornhytta', 1290], ['Steinbua', 760], ['Fugleredet', 1450]]
print(sorted(rader, key=lambda rad: rad[1], reverse=True))Utskrift:
[['Fugleredet', 1450], ['Bjornhytta', 1290], ['Steinbua', 760]]To andre måter å få samme resultat, begge riktige:
- sorted(...)[::-1] — sorter stigende og snu lista etterpå. Fungerer, men gir en subtil forskjell ved like verdier: stabiliteten snus også.
- key=lambda rad: -rad[k] — sorter på det negative tallet. Virker bare på tall, ikke på tekst.
reverse=True er den formen boka bruker, fordi den sier hva den gjør og virker på alt.
Et utsnitt av de n første radene i ei ferdig sortert liste. Sluttgrensen er eksklusiv, så [:3] gir indeks 0, 1 og 2.
sortert = [['Steinbua', 760], ['Skarvheim', 990], ['Snofonna', 990], ['Bjornhytta', 1290]]
print(sortert[:3])
print(len(sortert[:10]))Utskrift:
[['Steinbua', 760], ['Skarvheim', 990], ['Snofonna', 990]]
4Den snille egenskapen: er lista kortere enn n, får du bare det som finnes — ingen feilmelding. [:10] på fire rader gir fire rader. Det gjør formen trygg på tomme og korte tabeller, og det er en av få steder du ikke trenger en egen tomtest.
Rekkefølgen er hele forutsetningen: utsnittet tar de n første, uansett om de er de beste eller de verste. Sorter riktig vei først.
topp_n(tabell, kolonnenavn, n, storst) som returnerer de n beste radene på den oppgitte kolonnen. Er storst sann, skal de største komme først; ellers de minste.»Én funksjon, styrt av parameterne:
def topp_n(tabell, kolonnenavn, n, storst):
k = tabell[0].index(kolonnenavn)
sortert = sorted(tabell[1:], key=lambda rad: rad[k], reverse=storst)
return sortert[:n]
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
print('De tre billigste:')
for rad in topp_n(tabell, 'pris', 3, False):
print(rad[0].ljust(12) + str(rad[2]).rjust(6))
print('De tre største:')
for rad in topp_n(tabell, 'senger', 3, True):
print(rad[0].ljust(12) + str(rad[3]).rjust(6))Utskrift:
De tre billigste:
Steinbua 760
Skarvheim 990
Snofonna 990
De tre største:
Snofonna 40
Skarvheim 24
Bjornhytta 18Legg merke til reverse=storst. Parameteren sendes rett inn i sorted — ingen if-setning trengs, fordi reverse allerede tar imot en sannhetsverdi. Det er kortere og lettere å lese enn to nesten like grener.
En kortere kortliste enn du ba om:
def topp_n(tabell, kolonnenavn, n, storst):
k = tabell[0].index(kolonnenavn)
return sorted(tabell[1:], key=lambda rad: rad[k], reverse=storst)[:n]
tabell = [['navn', 'pris'], ['Steinbua', 760], ['Bjornhytta', 1290]]
print(len(topp_n(tabell, 'pris', 5, False)))Utskrift:
2To rader inn, to rader ut, ingen feilmelding. Det er utsnittets snille egenskap.
Slik ville sensor sett på det: at retningen er en parameter i stedet for to nesten like funksjoner, er gjenbruket som gir uttelling. Og at utsnittet tas etter sorteringen — ikke før — er det som gjør svaret riktig i det hele tatt.
Kantfeilen å nevne: ved like verdier på plass 3 og 4 er det tilfeldig hvem som kommer med i en toppliste, i den forstand at det avhenger av rekkefølgen i fila. Sier ikke oppgaven noe, skriver du én setning: «ved lik pris tas den som står først i fila».
dyreste_navn(tabell, n) som returnerer ei liste med navnene på de n dyreste fjellstuene, dyrest først.»Returverdien skal være ei flat liste med navn, ikke rader.
Hva skriver programmet ut? Forklar den andre linja med én setning.
rader = [['A', 3], ['B', 1], ['C', 2]]
print(sorted(rader, key=lambda rad: rad[1])[:2])
print(sorted(rader[:2], key=lambda rad: rad[1]))Løkke 3 — boblesortering på rader (~12 min)
Enkelte sett ber uttrykkelig om at du skriver sorteringen selv. Da er boblesorteringen fra kap. 6.1 svaret — den skal bare sammenligne én kolonne og bytte hele rader.
Fire linjer, og bare sammenligningen skiller den fra den vanlige boblesorteringen:
for i in range(len(rader) - 1):
for j in range(len(rader) - 1 - i):
if rader[j][k] > rader[j + 1][k]:
rader[j], rader[j + 1] = rader[j + 1], rader[j]Blokken over er mønsteret; den ferdige funksjonen står i eksempel 3.
To detaljer avgjør om den virker:
- rader[j][k] i sammenligningen — bare kolonnen k avgjør rekkefølgen.
- rader[j], rader[j+1] = … i byttet — hele radene bytter plass, ikke bare tallene. Skriver du rader[j][k], rader[j+1][k] = …, bytter du bare prisene og lar navnene stå igjen — og da er tabellen ødelagt.
Løkkegrensene er de samme som alltid: len - 1 passeringer, og len - 1 - i sammenligninger i hver, fordi halen allerede er sortert. Bruk < for synkende rekkefølge.
Boblesorteringen sorterer lista på plass — den returnerer ingenting av seg selv, og den som sendte lista inn, ser endringen.
Er det uønsket, tar du en kopi først:
original = [[3, 'A'], [1, 'B']]
kopi = original[:]
kopi[0], kopi[1] = kopi[1], kopi[0]
print(kopi)
print(original)Utskrift:
[[1, 'B'], [3, 'A']]
[[3, 'A'], [1, 'B']]original[:] gir ei ny ytre liste med de samme radene i. Det er nok her, siden byttet bare endrer den ytre rekkefølgen. Skulle du endre innholdet i en rad, ville begge listene sett det — det er aliasing, felle #15.
I praksis er det som regel greit å sortere på plass, siden analysefunksjonen din eier tabellen den har fått. Men si fra i en kommentar hvis du gjør det.
Rangér fjellstuene etter pris med (1) boblesortering, (2) sorted med key, og (3) en utlevert sort_list(tabell, k) slik oppgaveteksten kan gi den. Vis at de tre gir samme svar.
def bobler_paa_kolonne(rader, k):
for i in range(len(rader) - 1):
for j in range(len(rader) - 1 - i):
if rader[j][k] > rader[j + 1][k]:
rader[j], rader[j + 1] = rader[j + 1], rader[j]
return rader
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
for rad in bobler_paa_kolonne(tabell[1:], 2):
print(rad[0], rad[2])Utskrift:
Steinbua 760
Skarvheim 990
Snofonna 990
Bjornhytta 1290
Fugleredet 1450Måte 2 — snarveien:
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
for rad in sorted(tabell[1:], key=lambda rad: rad[2]):
print(rad[0], rad[2])Utskrift:
Steinbua 760
Skarvheim 990
Snofonna 990
Bjornhytta 1290
Fugleredet 1450Måte 3 — den utleverte funksjonen. Slik ser en typisk utlevering ut i oppgaveteksten, og slik bruker du den:
def sort_list(tabell, k):
return sorted(tabell, key=lambda rad: rad[k])
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
k = tabell[0].index('pris')
for rad in sort_list(tabell[1:], k):
print(rad[0], rad[2])Utskrift:
Steinbua 760
Skarvheim 990
Snofonna 990
Bjornhytta 1290
Fugleredet 1450Alle tre gir det samme svaret, og alle tre gir full pott. Det som skiller dem, er hva oppgaven ber om:
| Oppgaveteksten sier | Du skal |
|---|---|
| «sortér tabellen» (uten mer) | bruke sorted med key — kortest og tryggest |
| «skriv en sorteringsalgoritme» / «bruk boblesortering» | skrive de fire linjene selv |
«du kan bruke den gitte sort_list» | kalle den, og bruke tiden på resten |
Slik ville sensor sett på det: å skrive boblesortering når
sort_list er utlevert er ikke feil, men det koster deg fem–ti minutter du trengte til analysen. Å bruke sorted når oppgaven uttrykkelig ba om en egen algoritme, er derimot feil svar på bestillingen.Kantfeilen i måte 1: legg merke til at
bobler_paa_kolonne fikk tabell[1:] inn, ikke tabell. Hadde overskriftsraden vært med, ville 'pris' > 1290 gitt TypeError: '>' not supported between instances of 'str' and 'int'.Fem feller, og de tre første er stumme:
- Sortere kolonnen løsrevet fra radene. Du får tallene i rekkefølge, men ingen vet lenger hvilken rad de hørte til. Sorter alltid rader.
- #4 — hardkodet kolonneindeks. rad[2] svarer feil hvis kolonnene kommer i en annen rekkefølge. Bruk tabell[0].index(navn).
- Feil retning. «Dyreste» krever reverse=True. Skriv ut de to øverste radene og se på dem — det tar to sekunder.
- #17 — x = x.sort(). .sort() returnerer None, så variabelen din blir None. Bruk sorted(x) når du vil ha ei ny liste.
- Overskriftsraden med i sorteringen. Gir TypeError når tekst sammenlignes med tall, eller en overskrift midt i tabellen når kolonnen er tekst. Denne krasjer som regel, og er derfor den snilleste.
sorter_synkende(rader, k) som sorterer radene synkende på kolonne k med boblesortering. Du skal ikke bruke sorted eller .sort().»Test på sengekolonnen.
Tabellen har tre rader. Hvor mange ganger kjører den indre løkkekroppen til sammen, og hva skriver programmet ut?
rader = [['A', 3], ['B', 1], ['C', 2]]
antall = 0
for i in range(len(rader) - 1):
for j in range(len(rader) - 1 - i):
antall = antall + 1
if rader[j][1] > rader[j + 1][1]:
rader[j], rader[j + 1] = rader[j + 1], rader[j]
print(antall)
print(rader)Løkke 4 — rangér nøklene i en ordbok (~12 min)
Analysen i kap. 7.4 endte ofte i en ordbok: {kommune: antall}, {mekaniker: omsetning}. Neste deloppgave er som regel «vis dem i rekkefølge» — og da sorterer du nøklene etter verdien sin.
Gir nøklene som ei liste, sortert etter verdien de har i ordboka.
d = {'Vang': 2740, 'Hemsedal': 1980, 'Lom': 760}
print(sorted(d))
print(sorted(d, key=d.get))
print(sorted(d, key=d.get, reverse=True))Utskrift:
['Hemsedal', 'Lom', 'Vang']
['Lom', 'Hemsedal', 'Vang']
['Vang', 'Hemsedal', 'Lom']Den første linja sorterer alfabetisk på nøkkelen, de to neste på verdien. Det er key=d.get som gjør forskjellen: for hver nøkkel slås verdien opp, og den bestemmer rekkefølgen.
Du får nøklene tilbake, ikke verdiene. Verdien henter du med d[nøkkel] når du skal skrive den ut.
d.items() gir par av nøkkel og verdi, som kan sorteres som rader:d = {'Vang': 2740, 'Hemsedal': 1980, 'Lom': 760}
par = list(d.items())
print(sorted(par, key=lambda p: p[1], reverse=True))Utskrift:
[('Vang', 2740), ('Hemsedal', 1980), ('Lom', 760)]Hvert par er et tuppel — en uforanderlig sekvens, se kap. 3.6 — men det indekseres akkurat som ei liste, så p[0] er nøkkelen og p[1] er verdien.
Formen er nyttig når du skal skrive ut både nøkkel og verdi i sortert rekkefølge. sorted(d, key=d.get) er kortere når du bare trenger nøklene.
senger_per_kommune(tabell) som returnerer {kommune: sum senger}, og kommuner_rangert(tabell) som returnerer kommunenavnene sortert etter kapasitet, størst først. Skriv til slutt ut oversikten.»def senger_per_kommune(tabell):
k_kom = tabell[0].index('kommune')
k_sng = tabell[0].index('senger')
d = {}
for rad in tabell[1:]:
d[rad[k_kom]] = d.get(rad[k_kom], 0) + rad[k_sng]
return d
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
print(senger_per_kommune(tabell))Utskrift:
{'Vang': 30, 'Hemsedal': 64, 'Lom': 8}Rangeringen, som kaller den:
def senger_per_kommune(tabell):
k_kom = tabell[0].index('kommune')
k_sng = tabell[0].index('senger')
d = {}
for rad in tabell[1:]:
d[rad[k_kom]] = d.get(rad[k_kom], 0) + rad[k_sng]
return d
def kommuner_rangert(tabell):
d = senger_per_kommune(tabell)
return sorted(d, key=d.get, reverse=True)
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
print(kommuner_rangert(tabell))Utskrift:
['Hemsedal', 'Vang', 'Lom']Oversikten skrevet ut:
d = {'Vang': 30, 'Hemsedal': 64, 'Lom': 8}
for kommune in sorted(d, key=d.get, reverse=True):
print(kommune.ljust(12) + str(d[kommune]).rjust(4))Utskrift:
Hemsedal 64
Vang 30
Lom 8Slik ville sensor sett på det: at kommuner_rangert kaller senger_per_kommune er kjeden i praksis — to deloppgaver, der den andre bygger på den første. Det er nøyaktig det mønsteret sensor belønner.
Legg også merke til at rangeringen er to linjer. Er analysen fra forrige kapittel på plass, er sorteringsdeloppgaven som regel den billigste i hele settet.
Kantfeilen: ved lik kapasitet er rekkefølgen den nøklene ble satt inn i ordboka i, altså den de kom i fila. Det er forutsigbart, men ikke noe oppgaven har bedt om — én setning om antakelsen dekker deg.
{kommune: antall hytter}. Skriv rangert_liste(ordbok) som returnerer ei liste med par på formen [kommune, antall], sortert med flest hytter først.»Returverdien skal være ei liste av lister, ikke en ordbok.
Bruk fjellstuetabellen.
a) Skriv snitt_pris_per_kommune(tabell) som returnerer {kommune: gjennomsnittspris}.
b) Skriv billigste_kommune(tabell) som returnerer navnet på kommunen med lavest gjennomsnittspris, ved å gjenbruke a).
c) Skriv ut alle kommunene sortert etter gjennomsnittspris, billigst først, med prisen avrundet til hele kroner i utskriften.
Løkke 5 — flere kriterier og tabeller som ikke er ryddige (~11 min)
Til slutt de to tilfellene som skiller en god sortering fra en som bare virker på testdataene.
Skal like verdier skilles av et annet felt, gir key tilbake et tuppel i stedet for én verdi:
rader = [['Skarvheim', 'Hemsedal', 990], ['Snofonna', 'Hemsedal', 990],
['Steinbua', 'Lom', 760]]
print(sorted(rader, key=lambda rad: (rad[2], rad[0])))Utskrift:
[['Steinbua', 'Lom', 760], ['Skarvheim', 'Hemsedal', 990], ['Snofonna', 'Hemsedal', 990]]Tuppelet sammenlignes elementvis: først prisen, og bare når to priser er like, navnet. Skarvheim kommer før Snøfonna fordi 'Sk' kommer før 'Sn'.
Skal det andre kriteriet gå motsatt vei, går ikke reverse — den snur alt. Løsningen er å negere det numeriske feltet: key=lambda rad: (rad[2], -rad[3]). Det virker bare på tall.
Denne varianten kommer sjelden på eksamen, men den er den ryddige måten å svare på «sorter på pris, og ved lik pris alfabetisk».
Er kolonnen fortsatt tekst — fordi konverteringen i kap. 7.3 ble hoppet over — sorterer den tegn for tegn:
print(sorted(['1290', '990', '760']))
print(sorted([1290, 990, 760]))Utskrift:
['1290', '760', '990']
[760, 990, 1290]'1290' kommer først fordi '1' kommer før '7' og '9'. Det er feilkode #1, glemt konvertering, og den er stum: du får ei sortert liste, den er bare sortert etter noe annet enn du trodde.
Kontrollen tar to sekunder: se på det største og det minste tallet i svaret ditt. Står et firesifret tall før et tresifret, sorterer du tekst.
På noen sett står det i oppgaveteksten: «Du kan bruke funksjonen sort_list(tabell, k), som returnerer tabellen sortert stigende på kolonne k.»
Da skal du ikke skrive sorteringen selv. Poenget med deloppgaven er å vise at du
1. finner riktig k — helst med tabell[0].index(navn),
2. sender inn radene uten overskriftsraden,
3. bruker returverdien riktig, for eksempel med et utsnitt til slutt.
Å skrive boblesortering likevel gir samme svar, men koster deg minutter du trengte på analysen. Les alltid gjennom hele oppgaveteksten før du begynner å kode — utleveringene står ofte i innledningen, ikke i deloppgaven.
topp_tre_billigste(tabell) som returnerer de tre billigste fjellstuene. Funksjonen skal returnere ei tom liste hvis tabellen ikke har noen datarader, og den skal ikke anta at priskolonnen ligger på en bestemt plass.»def topp_tre_billigste(tabell):
if len(tabell) <= 1:
return []
k = tabell[0].index('pris')
return sorted(tabell[1:], key=lambda rad: rad[k])[:3]
tabell = [['navn', 'kommune', 'pris', 'senger'],
['Bjornhytta', 'Vang', 1290, 18],
['Skarvheim', 'Hemsedal', 990, 24],
['Fugleredet', 'Vang', 1450, 12],
['Snofonna', 'Hemsedal', 990, 40],
['Steinbua', 'Lom', 760, 8]]
for rad in topp_tre_billigste(tabell):
print(rad[0], rad[2])
print(topp_tre_billigste([['navn', 'kommune', 'pris', 'senger']]))Utskrift:
Steinbua 760
Skarvheim 990
Snofonna 990
[]Trengs tomtesten i det hele tatt? Nei — og det er verdt å vite:
tabell = [['navn', 'pris']]
k = tabell[0].index('pris')
print(sorted(tabell[1:], key=lambda rad: rad[k])[:3])Utskrift:
[]sorted på ei tom liste gir ei tom liste, og utsnittet av den er tom. Sorteringen er en av de få analysene som takler tomtilfellet helt av seg selv.
Hvorfor testen likevel står der: den gjør antakelsen synlig. En leser — og en sensor — ser med én gang at kandidaten har tenkt på tilfellet. Det koster to linjer og er billig forsikring når du er usikker.
Der testen derimot er nødvendig, er når du plukker ut noe etter sorteringen:
def billigste(tabell):
if len(tabell) <= 1:
return None
k = tabell[0].index('pris')
return sorted(tabell[1:], key=lambda rad: rad[k])[0]
print(billigste([['navn', 'pris'], ['Steinbua', 760]]))
print(billigste([['navn', 'pris']]))Utskrift:
['Steinbua', 760]
NoneHer ville [0] på ei tom liste gitt IndexError. Regelen: utsnitt er trygge, indekser er det ikke.
Slik ville sensor sett på det: det som gir de siste poengene, er kolonnen funnet ved navn og et bevisst, dokumentert svar på tomtilfellet. Robusthetskravene samlet står i kap. 7.6.
sortert_pris_og_navn(tabell) som returnerer radene sortert stigende på pris, og alfabetisk på navn når to priser er like.»Vis at rekkefølgen mellom de to 990-hyttene blir riktig.
Oppgaveteksten oppgir denne hjelpefunksjonen:
«sort_list(rader, k) returnerer radene sortert stigende på kolonne k.»
a) Skriv dyreste_tre(tabell) som returnerer de tre dyreste fjellstuene, dyrest først. Bruk sort_list — ikke sorted og ikke boblesortering.
b) Funksjonen skal returnere ei tom liste for en tabell uten datarader, og den skal ikke anta hvor priskolonnen ligger.
c) Skriv én setning om hvorfor det er riktig å bruke den utleverte funksjonen her.
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.
Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.