IlmHamroh
Python kursi/Malumot tuzilmalari3/18-dars27 daqiqa
Mundarija (23)

6.3-dars: Ro'yxatni saralash — sort va sorted

6-QISM — MA'LUMOT TUZILMALARI · 3-dars


1. Kirish va motivatsiya

Saralash — dasturlashdagi eng ko'p ishlatiladigan amallardan biri. Pythonda u juda oson ko'rinadi:

python
royxat = [3, 1, 2]
royxat.sort()
print(royxat)                   # [1, 2, 3]

Lekin haqiqiy vazifalar oddiy emas:

python
xodimlar = [
    {"ism": "Aziz",  "yosh": 30, "maosh": 5_000_000},
    {"ism": "Bobur", "yosh": 25, "maosh": 7_000_000},
    {"ism": "Aziza", "yosh": 30, "maosh": 6_000_000},
]

# Yosh bo'yicha saralang. Yoshlar teng bo'lsa — maosh bo'yicha kamayish tartibida.

Yoki:

python
sozlar = ["Olma", "anor", "Behi"]
sozlar.sort()
print(sozlar)                   # ['Behi', 'Olma', 'anor']  ← nega?!

Yoki:

python
royxat = [1, "2", 3]
royxat.sort()                   # ❌ TypeError

Bu darsda:

  • sort() vs sorted() — qachon qaysi biri
  • key funksiyasi — saralashning yuragi
  • Barqarorlik (stability) va ko'p mezonli saralash
  • reverse va teskari tartib nozikliklari
  • Timsort — Python algoritmi
  • functools.cmp_to_key, operator.itemgetter

2. Nazariya — chuqur tushuntirish

2.1. sort() vs sorted()

royxat.sort() sorted(iterable)
Nima list metodi Ichki funksiya
Ishlaydi Faqat list bilan Har qanday iterable
Natija None Yangi list
Asl O'zgaradi O'zgarmaydi
Xotira O(n) vaqtincha O(n) yangi
python
r = [3, 1, 2]

r.sort()                        # joyida
print(r)                        # [1, 2, 3]

r = [3, 1, 2]
yangi = sorted(r)               # yangi ro'yxat
print(r, yangi)                 # [3, 1, 2] [1, 2, 3]

sorted() har qanday iterable bilan:

python
sorted("cba")                   # ['a', 'b', 'c']  ← LIST qaytaradi
sorted({3, 1, 2})               # [1, 2, 3]
sorted({"b": 2, "a": 1})        # ['a', 'b']  ← kalitlar
sorted({"b": 2, "a": 1}.items())    # [('a', 1), ('b', 2)]
sorted(range(5, 0, -1))         # [1, 2, 3, 4, 5]
sorted(x for x in [3, 1, 2])    # [1, 2, 3]  ← generator

sorted() doim list qaytaradi — hatto satr yoki to'plamdan ham.

Qachon qaysi biri:

python
# Asl kerak emas, ro'yxat — sort() (biroz tejamli)
royxat.sort()

# Asl saqlanishi kerak — sorted()
saralangan = sorted(royxat)

# Ro'yxat emas — sorted()
saralangan = sorted(toplam)

# Funksiya argumentini o'zgartirmaslik kerak — sorted()
def eng_kattalari(malumot, n=3):
    return sorted(malumot, reverse=True)[:n]    # ✅ malumot o'zgarmaydi

2.2. key funksiyasi

key — har bir element uchun chaqiriladigan funksiya. Uning natijasi bo'yicha saralanadi.

python
sozlar = ["shaftoli", "olma", "banan"]

sorted(sozlar)                  # ['banan', 'olma', 'shaftoli'] — alifbo
sorted(sozlar, key=len)         # ['olma', 'banan', 'shaftoli'] — uzunlik

Qanday ishlaydi:

sozlar:    ["shaftoli", "olma",  "banan"]
key=len:   [    8,         4,       5   ]      ← key hisoblanadi
saralash:  [    4,         5,       8   ]      ← kalitlar saralanadi
natija:    ["olma",    "banan", "shaftoli"]    ← elementlar ko'chiriladi

Muhim: key har bir element uchun faqat bir marta chaqiriladi (decorate-sort-undecorate naqshi). Bu — samaradorlik uchun.

python
def qimmat_kalit(x):
    print(f"  key({x}) chaqirildi")
    return len(x)

sorted(["aa", "b", "ccc"], key=qimmat_kalit)
text
  key(aa) chaqirildi
  key(b) chaqirildi
  key(ccc) chaqirildi

3 element → 3 chaqiruv. Solishtirishlar esa ~n log n ta bo'lardi.

Keng tarqalgan key funksiyalari:

python
# Uzunlik
sorted(sozlar, key=len)

# Registrsiz (⚠️ casefold, lower emas — 4.4-dars)
sorted(sozlar, key=str.casefold)

# Lug'at maydoni
sorted(xodimlar, key=lambda x: x["yosh"])

# Obyekt atributi
sorted(xodimlar, key=lambda x: x.yosh)

# Absolyut qiymat
sorted([-3, 1, -2], key=abs)              # [1, -2, -3]

# Oxirgi belgi
sorted(sozlar, key=lambda s: s[-1])

# Bir nechta mezon (tuple!)
sorted(xodimlar, key=lambda x: (x["yosh"], x["ism"]))

operator moduli — tezroq:

python
from operator import itemgetter, attrgetter, methodcaller

sorted(xodimlar, key=itemgetter("yosh"))            # lug'at/tuple
sorted(xodimlar, key=itemgetter("yosh", "ism"))     # ko'p mezon
sorted(juftliklar, key=itemgetter(1))               # tuple 1-elementi
sorted(obyektlar, key=attrgetter("yosh"))           # atribut
sorted(sozlar, key=methodcaller("count", "a"))      # metod chaqirish

itemgetter — C da yozilgan, lambda dan ~20-30% tezroq.

2.3. Barqarorlik (stability)

Python saralashi barqaror — teng kalitli elementlar asl tartibini saqlaydi.

python
malumot = [("a", 2), ("b", 1), ("c", 2), ("d", 1)]

sorted(malumot, key=itemgetter(1))
# [('b', 1), ('d', 1), ('a', 2), ('c', 2)]
#    ↑         ↑         ↑         ↑
#   b, d asl tartibda; a, c asl tartibda

Bu — kafolatlangan xususiyat (til spetsifikatsiyasida yozilgan), tasodif emas.

Nega muhim — ko'p mezonli saralash:

Barqarorlik tufayli ketma-ket saralash ishlaydi:

python
# Yosh bo'yicha, teng bo'lsa — ism bo'yicha

# Usul 1: tuple kalit (bir o'tish) ✅
sorted(xodimlar, key=lambda x: (x["yosh"], x["ism"]))

# Usul 2: ketma-ket saralash (barqarorlik tufayli)
xodimlar.sort(key=itemgetter("ism"))        # AVVAL ikkilamchi
xodimlar.sort(key=itemgetter("yosh"))       # KEYIN asosiy

Tartib teskari! Ikkinchi usulda eng muhim mezon oxirida saralanadi.

Qachon 2-usul kerak:

Turli yo'nalishlarda saralash kerak bo'lganda:

python
# Yosh o'sish, maosh kamayish tartibida

# ❌ tuple bilan qiyin (turli yo'nalish)
sorted(xodimlar, key=lambda x: (x["yosh"], ???))

# ✅ ketma-ket
xodimlar.sort(key=itemgetter("maosh"), reverse=True)    # ikkilamchi
xodimlar.sort(key=itemgetter("yosh"))                   # asosiy

# ✅ Yoki sonlar uchun minus
sorted(xodimlar, key=lambda x: (x["yosh"], -x["maosh"]))

Minus faqat sonlar bilan ishlaydi:

python
sorted(x, key=lambda v: (-v["yosh"], v["ism"]))     # ✅ son
sorted(x, key=lambda v: (v["yosh"], -v["ism"]))     # ❌ TypeError — satr

Satr uchun teskari yo'nalish — faqat ketma-ket saralash.

2.4. reverse parametri

python
sorted([3, 1, 2], reverse=True)         # [3, 2, 1]
royxat.sort(reverse=True)

reverse=True ≠ sorted(...)[::-1] — barqarorlik boshqacha:

python
malumot = [("a", 1), ("b", 1)]

sorted(malumot, key=itemgetter(1), reverse=True)
# [('a', 1), ('b', 1)]  ← asl tartib SAQLANADI

sorted(malumot, key=itemgetter(1))[::-1]
# [('b', 1), ('a', 1)]  ← tartib TESKARI!

reverse=True barqarorlikni buzmaydi — u faqat solishtirish yo'nalishini o'zgartiradi. [::-1] esa hamma narsani teskari qiladi.

Shuning uchun doim reverse=True ishlating.

2.5. Nima saralanadi — solishtirish qoidalari

Saralash < operatoriga tayanadi. Element < ni qo'llab-quvvatlashi kerak.

Sonlar:

python
sorted([3, 1.5, 2])             # [1.5, 2, 3] — int va float aralash ✅

Satrlar — kod nuqtasi bo'yicha (4.8-dars):

python
sorted(["Olma", "anor", "Behi"])
# ['Behi', 'Olma', 'anor']
#  B=66     O=79    a=97

Katta harflar oldin keladi — chunki ASCII da A(65) < a(97).

python
sorted(["Olma", "anor", "Behi"], key=str.casefold)
# ['anor', 'Behi', 'Olma']  ✅

O'zbek harflari:

python
sorted(["shaftoli", "olma", "o'rik", "anor"])
# ['anor', 'olma', "o'rik", 'shaftoli']

# ⚠️ Lekin: o' va oʻ turlicha saralanadi (4.8-dars)
sorted(["oʻrik", "o'rik", "olma"])
# ['olma', "o'rik", 'oʻrik']   — U+0027 (39) < U+02BB (699)

Yechim — normalizatsiya:

python
def uzbek_kalit(s: str) -> str:
    return s.replace("ʻ", "'").replace("'", "'").casefold()

sorted(sozlar, key=uzbek_kalit)

Tuple va list — leksikografik:

python
sorted([(1, "b"), (1, "a"), (0, "z")])
# [(0, 'z'), (1, 'a'), (1, 'b')]

Avval 0-element, teng bo'lsa 1-element, ...

Turli tur — TypeError:

python
sorted([1, "2", 3])
# ❌ TypeError: '<' not supported between instances of 'str' and 'int'

sorted([1, None])
# ❌ TypeError

sorted([1, 2.5, True])
# ✅ [1, True, 2.5]  — bool int ning qism turi; True == 1, saralash barqaror
#                      bo'lgani uchun teng elementlar ASL tartibda qoladi

Bu — Python 3 o'zgarishi. Python 2 da har qanday tur solishtirilardi (natija ma'nosiz bo'lsa ham). Python 3 bu "jimgina noto'g'ri" xatti-harakatni yo'q qildi.

None bilan saralash:

python
malumot = [3, None, 1, None, 2]

# ❌ TypeError
sorted(malumot)

# ✅ None ni oxiriga
sorted(malumot, key=lambda x: (x is None, x))

# ✅ None ni boshiga
sorted(malumot, key=lambda x: (x is not None, x))

# ✅ None ni sukut qiymat bilan
sorted(malumot, key=lambda x: x if x is not None else 0)

(x is None, x) — hiyla: False < True, shuning uchun None bo'lmaganlar oldin.

Lekin bu None bo'lganda ikkinchi element None bo'ladi — teng True lar orasida solishtirish kerak bo'lmasa ishlaydi (barqarorlik tufayli teng kalitlar solishtirilmaydi).

Xavfsizroq:

python
sorted(malumot, key=lambda x: (x is None, x if x is not None else 0))

2.6. Timsort — Python algoritmi

Python Timsort ishlatadi — Tim Peters (Zen muallifi) 2002 yilda yaratgan.

Xususiyatlari:

Turi Gibrid: merge sort + insertion sort
Barqaror Ha
Eng yomon O(n log n)
Eng yaxshi O(n) — allaqachon saralangan
Xotira O(n)
Adaptiv Qisman saralangan ma'lumotda tez

Asosiy g'oya: haqiqiy ma'lumot ko'pincha qisman saralangan bo'ladi. Timsort tabiiy o'sish/kamayish ketma-ketliklarini (run) topadi va ularni birlashtiradi.

python
# Deyarli saralangan — juda tez
malumot = list(range(1_000_000))
malumot[500_000] = -1
malumot.sort()                  # ~O(n)

# Tasodifiy — O(n log n)
random.shuffle(malumot)
malumot.sort()

Timsort shunchalik yaxshiki, Java, Android, Swift, Rust ham uni qabul qilgan.

Amaliy xulosa: Python saralashi juda optimallashtirilgan. O'z algoritmingizni yozmang.

2.7. functools.cmp_to_key

Python 2 da sort(cmp=...) bor edi — ikkita elementni solishtiruvchi funksiya. Python 3 da olib tashlangan.

Agar eski solishtirish mantig'i kerak bo'lsa:

python
from functools import cmp_to_key

def solishtir(a, b):
    """Manfiy: a < b, 0: teng, musbat: a > b."""
    if len(a) != len(b):
        return len(a) - len(b)
    return (a > b) - (a < b)

sorted(sozlar, key=cmp_to_key(solishtir))

cmp_to_key sekin — har bir solishtirish uchun obyekt yaratadi. Deyarli har doim key bilan yozish mumkin:

python
# cmp o'rniga
sorted(sozlar, key=lambda s: (len(s), s))       # ✅ tezroq

cmp_to_key haqiqatan kerak bo'lgan holat: solishtirish tranzitiv bo'lmagan yoki kalit sifatida ifodalab bo'lmaydigan bo'lsa (juda kam uchraydi).

2.8. Qisman saralash — heapq

Faqat eng katta/kichik n ta kerak bo'lsa, to'liq saralash isrof:

python
import heapq

# ❌ O(n log n)
eng_kattalar = sorted(malumot, reverse=True)[:10]

# ✅ O(n log k)
eng_kattalar = heapq.nlargest(10, malumot)
eng_kichiklar = heapq.nsmallest(10, malumot)

# key bilan
heapq.nlargest(3, xodimlar, key=itemgetter("maosh"))

Qachon foydali:

n (jami) k (kerak) Tavsiya
Har qanday k = 1 max() / min()
Katta k << n heapq.nlargest
Har qanday k ≈ n sorted()

k = 1 bo'lsa max() eng tez.

2.9. Sinf obyektlarini saralash

8-qismda batafsil, hozircha asosiy:

python
from dataclasses import dataclass, field

@dataclass(order=True)          # ⭐ order=True — solishtirish metodlari
class Xodim:
    maosh: int
    ism: str

xodimlar = [Xodim(5000, "Aziz"), Xodim(3000, "Bobur")]
xodimlar.sort()                 # ✅ maosh, keyin ism bo'yicha

order=True maydon tartibiga qarab solishtiradi. Boshqacha kerak bo'lsa:

python
@dataclass(order=True)
class Xodim:
    saralash_kaliti: int = field(init=False, repr=False)
    ism: str
    maosh: int

    def __post_init__(self):
        self.saralash_kaliti = -self.maosh      # kamayish tartibida

Yoki oddiyroq — key ishlating:

python
xodimlar.sort(key=attrgetter("maosh"), reverse=True)

3. Tez ma'lumotnoma

sort vs sorted

python
royxat.sort()               joyida, None, faqat list
sorted(iterable)            yangi list, har qanday iterable

royxat.sort(key=..., reverse=...)
sorted(it, key=..., reverse=...)

key funksiyalari

python
key=len                     uzunlik
key=str.casefold            registrsiz ⭐
key=abs                     absolyut qiymat
key=lambda x: x["yosh"]     lug'at maydoni
key=itemgetter("yosh")      tezroq ⭐
key=attrgetter("yosh")      atribut
key=lambda x: (a, b)        ko'p mezon

Barqarorlik

python
Teng kalitlar asl tartibda qoladi — KAFOLATLANGAN

Ko'p mezon, turli yo'nalish:
    r.sort(key=ikkilamchi, reverse=True)    # AVVAL ikkilamchi
    r.sort(key=asosiy)                      # KEYIN asosiy

reverse=True   ✅ barqarorlikni saqlaydi
sorted(...)[::-1]  ❌ tartibni buzadi

Tuzoqlar

python
sorted(["Olma","anor"])         ['Olma','anor']  — katta harf oldin
sorted([1,"2"])                 ❌ TypeError
sorted([1,None])                ❌ TypeError
    → key=lambda x: (x is None, x)
saralangan = r.sort()           ❌ None

Tezroq muqobillar

python
max(r) / min(r)                 k=1
heapq.nlargest(k, r)            k << n
sorted(r)[:k]                   k ≈ n

4. Batafsil misollar

Misol 1 — key funksiyasi to'liq

python
"""key parametrining barcha shakllari."""

from operator import itemgetter, attrgetter, methodcaller
from dataclasses import dataclass

print("=== 1. Oddiy key funksiyalari ===")

SOZLAR = ["shaftoli", "Olma", "banan", "anor", "uzum"]
print(f"  Asl: {SOZLAR}\n")

VARIANTLAR = [
    ("key yo'q",            None),
    ("key=len",             len),
    ("key=str.casefold",    str.casefold),
    ("key=str.lower",       str.lower),
    ("oxirgi belgi",        lambda s: s[-1]),
    ("unli harflar soni",   lambda s: sum(c in "aeiou" for c in s.lower())),
    ("(uzunlik, alifbo)",   lambda s: (len(s), s.casefold())),
]

for nom, kalit in VARIANTLAR:
    natija = sorted(SOZLAR) if kalit is None else sorted(SOZLAR, key=kalit)
    print(f"  {nom:<20} {natija}")


print("\n\n=== 2. key faqat BIR MARTA chaqiriladi ===")

chaqiruvlar = []


def kuzatuvchi_kalit(x):
    chaqiruvlar.append(x)
    return len(x)


sorted(SOZLAR, key=kuzatuvchi_kalit)

print(f"  Elementlar soni:  {len(SOZLAR)}")
print(f"  key chaqiruvlari: {len(chaqiruvlar)}")
print(f"  Tartib: {chaqiruvlar}")
print(f"\n  ⭐ Har element uchun bir marta — decorate-sort-undecorate")


print("\n\n=== 3. Lug'atlar bilan ===")

XODIMLAR = [
    {"ism": "Aziz",  "yosh": 30, "maosh": 5_000_000, "bolim": "IT"},
    {"ism": "Bobur", "yosh": 25, "maosh": 7_000_000, "bolim": "Moliya"},
    {"ism": "Aziza", "yosh": 30, "maosh": 6_000_000, "bolim": "IT"},
    {"ism": "Dilnoza", "yosh": 25, "maosh": 5_000_000, "bolim": "HR"},
]


def chiqar(xodimlar, sarlavha):
    print(f"\n  {sarlavha}")
    print(f"    {'Ism':<10} {'Yosh':>5} {'Maosh':>12}  Bo'lim")
    print("    " + "─" * 42)
    for x in xodimlar:
        print(f"    {x['ism']:<10} {x['yosh']:>5} {x['maosh']:>12,}  {x['bolim']}")


chiqar(XODIMLAR, "Asl:")

chiqar(sorted(XODIMLAR, key=lambda x: x["yosh"]), "key=lambda x: x['yosh']:")
chiqar(sorted(XODIMLAR, key=itemgetter("maosh"), reverse=True),
       "itemgetter('maosh'), reverse=True:")
chiqar(sorted(XODIMLAR, key=itemgetter("yosh", "ism")),
       "itemgetter('yosh', 'ism') — ko'p mezon:")


print("\n\n=== 4. Turli yo'nalishlar ===")

print("\n  Vazifa: yosh O'SISH, maosh KAMAYISH tartibida\n")

# Usul 1: minus (faqat sonlar)
u1 = sorted(XODIMLAR, key=lambda x: (x["yosh"], -x["maosh"]))
chiqar(u1, "Usul 1: key=(yosh, -maosh)")

# Usul 2: ketma-ket saralash (barqarorlik)
u2 = XODIMLAR.copy()
u2.sort(key=itemgetter("maosh"), reverse=True)   # AVVAL ikkilamchi
u2.sort(key=itemgetter("yosh"))                  # KEYIN asosiy
chiqar(u2, "Usul 2: ketma-ket (ikkilamchi → asosiy)")

print(f"\n  Bir xilmi: {u1 == u2}")

print("\n  ⚠️ Satr uchun minus ishlamaydi:")
try:
    sorted(XODIMLAR, key=lambda x: (x["yosh"], -x["ism"]))
except TypeError as x:
    print(f"    key=(yosh, -ism) → TypeError: {x}")
print("    → Satrni teskari saralash uchun ketma-ket usul kerak")


print("\n\n=== 5. operator moduli ===")

import time

N = 200_000
MALUMOT = [{"a": i % 1000, "b": i} for i in range(N)]

USULLAR = [
    ("lambda x: x['a']",        lambda x: x["a"]),
    ("itemgetter('a')",         itemgetter("a")),
]

print(f"  {N:,} lug'atni saralash:\n")
natijalar = []
for nom, kalit in USULLAR:
    nusxa = MALUMOT.copy()
    boshlandi = time.perf_counter()
    nusxa.sort(key=kalit)
    natijalar.append((nom, time.perf_counter() - boshlandi))

eng_tez = min(v for _, v in natijalar)
for nom, vaqt in natijalar:
    print(f"    {nom:<24} {vaqt * 1000:>7.1f} ms  {vaqt / eng_tez:>5.2f}x")


print("\n\n=== 6. attrgetter va methodcaller ===")


@dataclass
class Mahsulot:
    nom: str
    narx: int
    ombor: int


MAHSULOTLAR = [
    Mahsulot("Non", 5_000, 100),
    Mahsulot("Sut", 12_000, 40),
    Mahsulot("Yog'", 45_000, 15),
]

print("  attrgetter('narx'):")
for m in sorted(MAHSULOTLAR, key=attrgetter("narx")):
    print(f"    {m.nom:<6} {m.narx:>8,} so'm  ({m.ombor} dona)")

print("\n  attrgetter('ombor', 'nom') — ko'p atribut:")
for m in sorted(MAHSULOTLAR, key=attrgetter("ombor", "nom")):
    print(f"    {m.nom:<6} {m.ombor:>4} dona")

print("\n  methodcaller('count', 'o'):")
for s in sorted(["olmalar", "non", "shokolad"], key=methodcaller("count", "o")):
    print(f"    {s:<10} 'o' soni: {s.count('o')}")

Natijaning muhim qismi:

text
=== 1. Oddiy key funksiyalari ===
  Asl: ['shaftoli', 'Olma', 'banan', 'anor', 'uzum']

  key yo'q             ['Olma', 'anor', 'banan', 'shaftoli', 'uzum']
  key=len              ['Olma', 'anor', 'uzum', 'banan', 'shaftoli']
  key=str.casefold     ['anor', 'banan', 'Olma', 'shaftoli', 'uzum']

=== 2. key faqat BIR MARTA chaqiriladi ===
  Elementlar soni:  5
  key chaqiruvlari: 5
  ⭐ Har element uchun bir marta — decorate-sort-undecorate

=== 5. operator moduli ===
  200,000 lug'atni saralash:

    lambda x: x['a']            84.3 ms   1.31x
    itemgetter('a')             64.2 ms   1.00x

Nima ko'rsatdi: 2.2, 2.3-bo'limlar.

Misol 2 — Barqarorlik

python
"""Barqarorlik nima va nega muhim."""

from operator import itemgetter

print("=== 1. Barqarorlik namoyishi ===")

MALUMOT = [
    ("Aziz",    "IT"),
    ("Bobur",   "HR"),
    ("Aziza",   "IT"),
    ("Dilnoza", "HR"),
    ("Eldor",   "IT"),
]

print("  Asl tartib:")
for ism, bolim in MALUMOT:
    print(f"    {ism:<10} {bolim}")

print("\n  Bo'lim bo'yicha saralangandan keyin:")
for ism, bolim in sorted(MALUMOT, key=itemgetter(1)):
    print(f"    {ism:<10} {bolim}")

print("""
  ⭐ HR ichida:  Bobur, Dilnoza  — asl tartibda
     IT ichida:  Aziz, Aziza, Eldor — asl tartibda

  Bu KAFOLATLANGAN, tasodif emas.
""")


print("\n=== 2. Ko'p mezon: ikki usul ===")

XODIMLAR = [
    {"ism": "Aziz",    "bolim": "IT",     "maosh": 5_000_000},
    {"ism": "Bobur",   "bolim": "Moliya", "maosh": 7_000_000},
    {"ism": "Aziza",   "bolim": "IT",     "maosh": 8_000_000},
    {"ism": "Dilnoza", "bolim": "HR",     "maosh": 5_000_000},
    {"ism": "Eldor",   "bolim": "IT",     "maosh": 5_000_000},
    {"ism": "Feruza",  "bolim": "Moliya", "maosh": 9_000_000},
]


def chiqar(xodimlar, sarlavha):
    print(f"\n  {sarlavha}")
    for x in xodimlar:
        print(f"    {x['bolim']:<8} {x['ism']:<10} {x['maosh']:>10,}")


print("\n  Vazifa: bo'lim bo'yicha, ichida maosh KAMAYISH tartibida")

# Usul 1: tuple + minus
u1 = sorted(XODIMLAR, key=lambda x: (x["bolim"], -x["maosh"]))
chiqar(u1, "Usul 1: (bolim, -maosh)")

# Usul 2: ketma-ket
u2 = XODIMLAR.copy()
u2.sort(key=itemgetter("maosh"), reverse=True)
u2.sort(key=itemgetter("bolim"))
chiqar(u2, "Usul 2: ketma-ket")

print(f"\n  Bir xilmi: {u1 == u2}")

print("""
  ⚠️ Ketma-ket usulda TARTIB TESKARI:
     avval ENG KAM muhim mezon
     oxirida ENG MUHIM mezon
""")


print("\n=== 3. reverse=True vs [::-1] ===")

MALUMOT2 = [("a", 1), ("b", 1), ("c", 2), ("d", 2)]
print(f"  Asl: {MALUMOT2}\n")

r1 = sorted(MALUMOT2, key=itemgetter(1), reverse=True)
r2 = sorted(MALUMOT2, key=itemgetter(1))[::-1]

print(f"  reverse=True:        {r1}")
print(f"  sorted(...)[::-1]:   {r2}")
print(f"  Bir xilmi:           {r1 == r2}")

print("""
  ⭐ reverse=True — barqarorlikni SAQLAYDI:
       teng kalitlar asl tartibda (a, b va c, d)

  ❌ [::-1] — HAMMASINI teskari qiladi:
       teng kalitlar ham teskari (b, a va d, c)

  → Doim reverse=True ishlating
""")


print("\n=== 4. Barqarorlik amaliyotda: jadval ustunlari ===")

TALABALAR = [
    {"ism": "Aziz",    "guruh": "A", "ball": 85},
    {"ism": "Bobur",   "guruh": "B", "ball": 92},
    {"ism": "Aziza",   "guruh": "A", "ball": 92},
    {"ism": "Dilnoza", "guruh": "B", "ball": 78},
    {"ism": "Eldor",   "guruh": "A", "ball": 85},
]


class Jadval:
    """Foydalanuvchi ustun sarlavhasini bosgandagi xatti-harakat."""

    def __init__(self, qatorlar):
        self.qatorlar = list(qatorlar)
        self.tarix = []

    def sarala(self, ustun, kamayish=False):
        """Barqarorlik tufayli oldingi saralash 'eslab qolinadi'."""
        self.qatorlar.sort(key=itemgetter(ustun), reverse=kamayish)
        self.tarix.append(f"{ustun}{'↓' if kamayish else '↑'}")
        return self

    def chiqar(self):
        yol = " → ".join(self.tarix) or "(saralanmagan)"
        print(f"\n  Bosilgan ustunlar: {yol}")
        print(f"    {'Ism':<10} {'Guruh':<7} {'Ball':>5}")
        print("    " + "─" * 24)
        for q in self.qatorlar:
            print(f"    {q['ism']:<10} {q['guruh']:<7} {q['ball']:>5}")


j = Jadval(TALABALAR)
j.chiqar()

j.sarala("ball", kamayish=True).chiqar()
j.sarala("guruh").chiqar()

print("""
  ⭐ Foydalanuvchi 'ball' ni bosdi, keyin 'guruh' ni bosdi
     → guruh bo'yicha, ichida ball kamayish tartibida

  Bu — barqarorlikning eng foydali qo'llanishi.
  Hech qanday qo'shimcha kod kerak emas.
""")


print("\n=== 5. Barqarorlik va o'zgaruvchan kalitlar ===")

print("""  ⚠️ Saralash paytida ro'yxatni o'zgartirmang:""")

r = [3, 1, 2]


class Yomon:
    def __init__(self, q, royxat):
        self.q = q
        self.royxat = royxat

    def __lt__(self, boshqa):
        self.royxat.append(99)          # ⚠️ saralash paytida o'zgartirish
        return self.q < boshqa.q


royxat = []
royxat.extend(Yomon(i, royxat) for i in [3, 1, 2])
try:
    royxat.sort()
    print(f"    Natija uzunligi: {len(royxat)} (kutilgan 3)")
except ValueError as x:
    print(f"    ValueError: {x}")

print("""
  Python saralash paytida ro'yxatni vaqtincha BO'SH qiladi —
  bu o'zgartirishlarni aniqlash uchun.
  Ba'zi holatlarda "list modified during sort" xatosi chiqadi.
""")

Natijaning muhim qismi:

text
=== 3. reverse=True vs [::-1] ===
  Asl: [('a', 1), ('b', 1), ('c', 2), ('d', 2)]

  reverse=True:        [('c', 2), ('d', 2), ('a', 1), ('b', 1)]
  sorted(...)[::-1]:   [('d', 2), ('c', 2), ('b', 1), ('a', 1)]
  Bir xilmi:           False

=== 4. Barqarorlik amaliyotda: jadval ustunlari ===

  Bosilgan ustunlar: ball↓ → guruh↑
    Ism        Guruh    Ball
    ────────────────────────
    Aziza      A          92
    Aziz       A          85
    Eldor      A          85
    Bobur      B          92
    Dilnoza    B          78

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 3 — Tuzoqlar

python
"""Saralashdagi keng tarqalgan tuzoqlar."""

print("=== 1. Katta va kichik harflar ===")

SOZLAR = ["Olma", "anor", "Behi", "shaftoli", "Uzum"]
print(f"  Asl: {SOZLAR}\n")

print(f"  sorted():              {sorted(SOZLAR)}")
print(f"  key=str.lower:         {sorted(SOZLAR, key=str.lower)}")
print(f"  key=str.casefold:      {sorted(SOZLAR, key=str.casefold)}")

print("\n  Nega? Kod nuqtalari:")
for s in ["Olma", "anor", "Behi"]:
    print(f"    {s:<10} birinchi belgi {s[0]!r} = {ord(s[0])}")
print("\n  ⭐ Katta harflar (65-90) < kichik harflar (97-122)")


print("\n\n=== 2. O'zbek apostroflari ===")

APOSTROFLAR = ["oʻrik", "o'rik", "o'rik", "olma", "orzu"]

print("  Ro'yxat:")
for s in APOSTROFLAR:
    kodlar = " ".join(f"U+{ord(c):04X}" for c in s)
    print(f"    {s:<8} {kodlar}")

print(f"\n  sorted():        {sorted(APOSTROFLAR)}")


def uzbek_kalit(s: str) -> str:
    """Apostrof variantlarini birlashtirish."""
    for x in "ʻʼ''`":
        s = s.replace(x, "'")
    return s.casefold()


print(f"  uzbek_kalit:     {sorted(APOSTROFLAR, key=uzbek_kalit)}")
print("\n  ⭐ Apostrof variantlari turli kod nuqtalari — normalizatsiya kerak")


print("\n\n=== 3. Turli turlar ===")

SINOVLAR = [
    ("[3, 1, 2]",           [3, 1, 2]),
    ("[3, 1.5, 2]",         [3, 1.5, 2]),
    ("[1, True, 2.5]",      [1, True, 2.5]),
    ("[1, '2', 3]",         [1, "2", 3]),
    ("[1, None, 2]",        [1, None, 2]),
    ("[[1], [2]]",          [[1], [2]]),
    ("[(1,2), (1,1)]",      [(1, 2), (1, 1)]),
    ("[{1}, {2}]",          [{1}, {2}]),
]

for nom, malumot in SINOVLAR:
    try:
        natija = str(sorted(malumot))
    except TypeError as x:
        natija = f"❌ TypeError: {str(x)[:44]}"
    print(f"  {nom:<18} → {natija}")

print("\n  ⚠️ {1} va {2} — to'plamlar qism-to'plam bo'yicha solishtiriladi")
print("     (qisman tartib — natija kutilmagan bo'lishi mumkin)")


print("\n\n=== 4. None bilan saralash ===")

MALUMOT = [3, None, 1, None, 2]
print(f"  Ro'yxat: {MALUMOT}\n")

try:
    sorted(MALUMOT)
except TypeError as x:
    print(f"  sorted() → TypeError: {x}\n")

YECHIMLAR = [
    ("None oxirida",  lambda x: (x is None, x if x is not None else 0)),
    ("None boshida",  lambda x: (x is not None, x if x is not None else 0)),
    ("None → 0",      lambda x: x if x is not None else 0),
    ("None → -inf",   lambda x: x if x is not None else float("-inf")),
]

for nom, kalit in YECHIMLAR:
    print(f"  {nom:<16} {sorted(MALUMOT, key=kalit)}")

print("\n  ⭐ (x is None, x) hiylasi: False(0) < True(1)")


print("\n\n=== 5. sort() natijasi ===")

r = [3, 1, 2]
natija = r.sort()
print(f"  natija = r.sort()")
print(f"    natija = {natija}")
print(f"    r      = {r}")

try:
    [3, 1, 2].sort().reverse()
except AttributeError as x:
    print(f"\n  Zanjirlash: AttributeError: {x}")

print("\n  ✅ To'g'ri:")
print(f"    sorted([3,1,2], reverse=True) → {sorted([3, 1, 2], reverse=True)}")


print("\n\n=== 6. Sonlar satr sifatida ===")

SONLAR = ["10", "9", "100", "2"]
print(f"  Satrlar: {SONLAR}\n")

print(f"  sorted():            {sorted(SONLAR)}  ← alifbo bo'yicha!")
print(f"  key=int:             {sorted(SONLAR, key=int)}  ✅")

FAYLLAR = ["fayl10.txt", "fayl9.txt", "fayl100.txt", "fayl2.txt"]
print(f"\n  Fayllar: {FAYLLAR}\n")
print(f"  sorted():            {sorted(FAYLLAR)}")

import re


def tabiiy_kalit(s: str):
    """Raqamlarni son sifatida solishtiradi (natural sort)."""
    return [int(q) if q.isdigit() else q.casefold()
            for q in re.split(r"(\d+)", s)]


print(f"  key=tabiiy_kalit:    {sorted(FAYLLAR, key=tabiiy_kalit)}  ✅")


print("\n\n=== 7. Float va NaN ===")

NAN = float("nan")
MALUMOT = [3.0, NAN, 1.0, 2.0]

print(f"  Ro'yxat: {MALUMOT}")
print(f"  sorted(): {sorted(MALUMOT)}  ← natija KUTILMAGAN!")

print(f"""
  Nega? NaN har qanday solishtirishda False qaytaradi:
    NaN < 1  → {NAN < 1}
    NaN > 1  → {NAN > 1}
    NaN == NaN → {NAN == NAN}

  Saralash algoritmi buzilib qoladi — xato bermaydi, jimgina noto'g'ri.
""")

import math
print(f"  ✅ NaN ni filtrlang:")
toza = [x for x in MALUMOT if not math.isnan(x)]
print(f"    {sorted(toza)}")

Natijaning muhim qismi:

text
=== 3. Turli turlar ===
  [3, 1, 2]          → [1, 2, 3]
  [1, True, 2.5]     → [1, True, 2.5]
  [1, '2', 3]        → ❌ TypeError: '<' not supported between insta
  [1, None, 2]       → ❌ TypeError: '<' not supported between insta

=== 6. Sonlar satr sifatida ===
  Satrlar: ['10', '9', '100', '2']

  sorted():            ['10', '100', '2', '9']  ← alifbo bo'yicha!
  key=int:             ['2', '9', '10', '100']  ✅

  Fayllar: ['fayl10.txt', 'fayl9.txt', 'fayl100.txt', 'fayl2.txt']

  sorted():            ['fayl10.txt', 'fayl100.txt', 'fayl2.txt', 'fayl9.txt']
  key=tabiiy_kalit:    ['fayl2.txt', 'fayl9.txt', 'fayl10.txt', 'fayl100.txt']  ✅

=== 7. Float va NaN ===
  sorted(): [3.0, nan, 1.0, 2.0]  ← natija KUTILMAGAN!

Nima ko'rsatdi: 2.5-bo'lim.

Misol 4 — Amaliy: hisobot generatori

python
"""Ko'p mezonli saralash bilan to'liq hisobot."""

from dataclasses import dataclass
from datetime import date
from operator import attrgetter
import heapq
import time
import random


@dataclass(frozen=True)
class Buyurtma:
    raqam: int
    mijoz: str
    summa: int
    sana: date
    holat: str          # "yangi" | "jarayonda" | "yakunlangan" | "bekor"


HOLAT_TARTIBI = {"yangi": 0, "jarayonda": 1, "yakunlangan": 2, "bekor": 3}

MIJOZLAR = ["Aziz", "Bobur", "Aziza", "Dilnoza", "Eldor", "Feruza"]
HOLATLAR = list(HOLAT_TARTIBI)

random.seed(42)
BUYURTMALAR = [
    Buyurtma(
        raqam=1000 + i,
        mijoz=random.choice(MIJOZLAR),
        summa=random.randrange(50_000, 5_000_000, 10_000),
        sana=date(2026, random.randint(1, 9), random.randint(1, 28)),
        holat=random.choice(HOLATLAR),
    )
    for i in range(40)
]


def jadval(buyurtmalar, sarlavha, n=None):
    korsatiladi = buyurtmalar[:n] if n else buyurtmalar
    qolgan = len(buyurtmalar) - len(korsatiladi)

    print(f"\n  ┌─ {sarlavha}")
    print(f"  │  {'№':<6} {'Mijoz':<10} {'Summa':>12} {'Sana':<12} Holat")
    print(f"  │  {'─' * 56}")
    for b in korsatiladi:
        print(f"  │  {b.raqam:<6} {b.mijoz:<10} {b.summa:>12,} "
              f"{b.sana.isoformat():<12} {b.holat}")
    if qolgan:
        print(f"  │  ... yana {qolgan} ta")
    print(f"  └─")


print("=== 1. Bir mezon bo'yicha ===")

jadval(sorted(BUYURTMALAR, key=attrgetter("summa"), reverse=True),
       "Eng katta summalar", 5)

jadval(sorted(BUYURTMALAR, key=attrgetter("sana")),
       "Eng eski buyurtmalar", 5)


print("\n\n=== 2. Ko'p mezon: tuple kalit ===")

jadval(
    sorted(BUYURTMALAR, key=lambda b: (HOLAT_TARTIBI[b.holat], -b.summa)),
    "Holat bo'yicha, ichida summa kamayish tartibida",
    12,
)

print("""
  key = (HOLAT_TARTIBI[b.holat], -b.summa)
        ↑ maxsus tartib (alifbo emas)   ↑ minus = kamayish
""")


print("\n=== 3. Ko'p mezon: ketma-ket saralash ===")

nusxa = list(BUYURTMALAR)
nusxa.sort(key=attrgetter("sana"), reverse=True)         # 3-mezon
nusxa.sort(key=attrgetter("summa"), reverse=True)        # 2-mezon
nusxa.sort(key=lambda b: HOLAT_TARTIBI[b.holat])         # 1-mezon

jadval(nusxa, "Holat → summa↓ → sana↓ (ketma-ket)", 12)

print("""
  ⚠️ Tartib TESKARI: eng kam muhim mezon AVVAL saralanadi.
     Barqarorlik tufayli oldingi tartib saqlanadi.
""")


print("\n=== 4. Guruhlab hisobot ===")

from itertools import groupby

# groupby SARALANGAN ma'lumot talab qiladi!
saralangan = sorted(BUYURTMALAR, key=attrgetter("mijoz"))

print(f"\n  {'Mijoz':<10} {'Soni':>5} {'Jami summa':>14} {'O''rtacha':>12}")
print("  " + "─" * 46)

qatorlar = []
for mijoz, guruh in groupby(saralangan, key=attrgetter("mijoz")):
    ruyxat = list(guruh)
    jami = sum(b.summa for b in ruyxat)
    qatorlar.append((mijoz, len(ruyxat), jami, jami // len(ruyxat)))

# Jami summa bo'yicha saralash
for mijoz, soni, jami, ortacha in sorted(qatorlar, key=lambda q: -q[2]):
    print(f"  {mijoz:<10} {soni:>5} {jami:>14,} {ortacha:>12,}")

print("\n  ⚠️ itertools.groupby SARALANGAN ma'lumot talab qiladi")
print("     (u faqat qo'shni bir xil elementlarni guruhlaydi)")


print("\n\n=== 5. Qisman saralash: heapq ===")

N = 500_000
KATTA = [random.randrange(10**9) for _ in range(N)]
K = 10

usullar = []

boshlandi = time.perf_counter()
r1 = sorted(KATTA, reverse=True)[:K]
usullar.append((f"sorted()[:{K}]", time.perf_counter() - boshlandi))

boshlandi = time.perf_counter()
r2 = heapq.nlargest(K, KATTA)
usullar.append((f"heapq.nlargest({K})", time.perf_counter() - boshlandi))

boshlandi = time.perf_counter()
r3 = [max(KATTA)]
usullar.append(("max()", time.perf_counter() - boshlandi))

assert r1 == r2
assert r3[0] == r1[0]

eng_tez = min(v for _, v in usullar)
print(f"  {N:,} sondan eng katta {K} ta:\n")
print(f"  {'Usul':<22} {'Vaqt':>10} {'Nisbat':>9}")
print("  " + "─" * 44)
for nom, vaqt in sorted(usullar, key=lambda x: x[1]):
    print(f"  {nom:<22} {vaqt * 1000:>7.1f} ms {vaqt / eng_tez:>8.1f}x")

print(f"""
  Tavsiya:
    k = 1        → max() / min()
    k << n       → heapq.nlargest(k, ...)
    k ≈ n        → sorted(...)[:k]
""")


print("\n=== 6. Timsort adaptivligi ===")

M = 1_000_000
SINOVLAR = [
    ("Allaqachon saralangan",   list(range(M))),
    ("Teskari saralangan",      list(range(M, 0, -1))),
    ("Deyarli saralangan",      list(range(M))),
    ("Bloklarga bo'lingan",     None),
    ("Tasodifiy",               None),
]

# Deyarli saralangan — 100 ta elementni almashtirish
deyarli = SINOVLAR[2][1]
for _ in range(100):
    i, j = random.randrange(M), random.randrange(M)
    deyarli[i], deyarli[j] = deyarli[j], deyarli[i]

# Bloklarga bo'lingan — 10 ta saralangan blok
bloklar = []
for b in range(10):
    bloklar.extend(range(b * M // 10, (b + 1) * M // 10))
SINOVLAR[3] = ("Bloklarga bo'lingan", bloklar)

tasodifiy = list(range(M))
random.shuffle(tasodifiy)
SINOVLAR[4] = ("Tasodifiy", tasodifiy)

print(f"  {M:,} element:\n")
print(f"  {'Ma`lumot turi':<26} {'Vaqt':>10} {'Nisbat':>9}")
print("  " + "─" * 48)

natijalar = []
for nom, malumot in SINOVLAR:
    nusxa = malumot.copy()
    boshlandi = time.perf_counter()
    nusxa.sort()
    natijalar.append((nom, time.perf_counter() - boshlandi))

eng_tez = min(v for _, v in natijalar)
for nom, vaqt in natijalar:
    print(f"  {nom:<26} {vaqt * 1000:>7.1f} ms {vaqt / eng_tez:>8.1f}x")

print("""
  ⭐ Timsort ADAPTIV:
     saralangan ma'lumotda O(n),
     tasodifiyda O(n log n).

     Haqiqiy ma'lumot ko'pincha qisman saralangan —
     shuning uchun Timsort amalda juda tez.
""")

Natijaning muhim qismi:

text
=== 5. Qisman saralash: heapq ===
  500,000 sondan eng katta 10 ta:

  Usul                        Vaqt    Nisbat
  ────────────────────────────────────────────
  max()                        8.4 ms      1.0x
  heapq.nlargest(10)          31.2 ms      3.7x
  sorted()[:10]              182.7 ms     21.7x

=== 6. Timsort adaptivligi ===
  1,000,000 element:

  Ma`lumot turi                    Vaqt    Nisbat
  ────────────────────────────────────────────────
  Allaqachon saralangan            5.1 ms      1.0x
  Teskari saralangan               6.3 ms      1.2x
  Deyarli saralangan              24.8 ms      4.9x
  Bloklarga bo'lingan             41.2 ms      8.1x
  Tasodifiy                      412.6 ms     80.9x

Nima ko'rsatdi: 2.3, 2.6, 2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"sort() saralangan ro'yxat qaytaradi" None. sorted() qaytaradi
"sorted() asl turni qaytaradi" Doim list — hatto satrdan ham
"key har solishtirish uchun chaqiriladi" Har element uchun bir marta
"reverse=True = sorted()[::-1]" Barqarorlik boshqacha
"Barqarorlik — amalga oshirish tafsiloti" Kafolatlangan xususiyat
"Ketma-ket saralashda asosiy mezon birinchi" Oxirgi
"sorted(["Olma", "anor"]) alifbo bo'yicha" Kod nuqtasi bo'yicha: O(79) < a(97)
"Har qanday ro'yxatni saralash mumkin" Turlar solishtirilishi kerak
"Python sortni tezlashtirish mumkin" Timsort — juda optimallashtirilgan

6. Keng tarqalgan xatolar va yechimlari

1. sort() natijasini ishlatish

python
saralangan = royxat.sort()      # ❌ None
saralangan = sorted(royxat)     # ✅

2. Registr muammosi

python
sorted(sozlar)                          # ❌ 'Olma' < 'anor'
sorted(sozlar, key=str.casefold)        # ✅

3. Sonlar satr sifatida

python
sorted(["10", "9", "2"])                # ❌ ['10', '2', '9']
sorted(["10", "9", "2"], key=int)       # ✅ ['2', '9', '10']

4. Ketma-ket saralashda tartib

python
r.sort(key=asosiy)                      # ❌ noto'g'ri tartib
r.sort(key=ikkilamchi)

r.sort(key=ikkilamchi)                  # ✅ avval ikkilamchi
r.sort(key=asosiy)

5. [::-1] barqarorlikni buzadi

python
sorted(r, key=k)[::-1]                  # ❌
sorted(r, key=k, reverse=True)          # ✅

6. None bilan

python
sorted([1, None, 2])                    # ❌ TypeError
sorted(m, key=lambda x: (x is None, x or 0))    # ✅

7. groupby saralanmagan ma'lumotda

python
groupby(malumot, key=k)                 # ❌ noto'g'ri guruhlar
groupby(sorted(malumot, key=k), key=k)  # ✅

8. Katta ro'yxatdan bir nechta element

python
eng_kattalar = sorted(katta, reverse=True)[:10]     # ❌ O(n log n)
eng_kattalar = heapq.nlargest(10, katta)            # ✅ O(n log k)

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6.2-dars (o'tilgan): sort va reverse metodlari
  • 4.8-dars (o'tilgan): Unicode va kod nuqtalari — saralash tartibi
  • 7-qism: lambda va yuqori tartibli funksiyalar
  • 8-qism: __lt__, functools.total_ordering, dataclass(order=True)
  • 10, 15-qismlar: operator moduli, funksional dasturlash
  • 15-qism: heapq, itertools.groupby, collections
  • Ma'lumotlar bazasi: ORDER BY — bir xil tushuncha

8. Eng yaxshi amaliyotlar

  1. sorted() — sukut tanlov. Asl o'zgarmaydi, har qanday iterable bilan ishlaydi. sort() faqat aniq joyida o'zgartirish kerak bo'lganda.

  2. key ishlating, cmp emas. key — har element uchun bir marta, cmp_to_key — har solishtirish uchun.

  3. Ko'p mezon uchun tuple kalit. key=lambda x: (a, b, c) — bir o'tishda.

  4. Turli yo'nalish — ketma-ket saralash. Eng kam muhim mezondan boshlab.

  5. reverse=True, [::-1] emas. Barqarorlik saqlanadi.

  6. Satrlar uchun key=str.casefold. O'zbekcha uchun apostrof normalizatsiyasi ham.

  7. operator.itemgetter / attrgetter. lambda dan tezroq va o'qilishi oson.

  8. k << n bo'lsa heapq. To'liq saralash isrof.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
1.  print(sorted([3, 1, 2]))
2.  print([3, 1, 2].sort())
3.  print(sorted("cba"))
4.  print(sorted(["Olma", "anor"]))
5.  print(sorted(["10", "9", "2"]))
6.  print(sorted([-3, 1, -2], key=abs))
7.  print(sorted([("a",1),("b",1)], key=lambda x: x[1], reverse=True))
8.  print(sorted([("a",1),("b",1)], key=lambda x: x[1])[::-1])
9.  print(sorted({"b": 2, "a": 1}))
10. print(sorted([1, True, 2.0]))
Javoblar
  1. [1, 2, 3]
  2. None
  3. ['a', 'b', 'c'] — list, satr emas
  4. ['Olma', 'anor'] — O(79) < a(97)
  5. ['10', '2', '9'] — alifbo bo'yicha
  6. [1, -2, -3]
  7. [('a', 1), ('b', 1)] — barqaror
  8. [('b', 1), ('a', 1)] — teskari
  9. ['a', 'b'] — kalitlar
  10. [1, True, 2.0] — True == 1, barqarorlik tufayli asl tartib saqlanadi (1 oldin kelgan edi)

Vazifa 2: key yozing

Har biri uchun key funksiyasi:

  1. So'zlarni oxirgi harfi bo'yicha
  2. Sonlarni raqamlari yig'indisi bo'yicha
  3. Fayllarni kengaytmasi, keyin nomi bo'yicha
  4. Ismlarni familiya bo'yicha ("Aziz Karimov")
  5. Versiyalarni to'g'ri ("1.10.0" > "1.9.0")
  6. Sanalarni satr shaklida ("05.03.2026")
Javoblar
python
1. key=lambda s: s[-1]
2. key=lambda n: sum(int(c) for c in str(abs(n)))
3. key=lambda f: (f.rsplit(".", 1)[-1], f)
4. key=lambda i: i.split()[-1]
5. key=lambda v: tuple(int(x) for x in v.split("."))
6. key=lambda d: tuple(reversed(d.split(".")))

Vazifa 3: Ko'p mezonli saralash

Xodimlar ro'yxatini saralang:

  1. Bo'lim (alifbo) → maosh (kamayish) → ism (alifbo)
  2. Bir usulni tuple kalit bilan, ikkinchisini ketma-ket saralash bilan
  3. Natijalar bir xilligini tekshiring
  4. Tezlikni solishtiring

Vazifa 4: Tabiiy saralash

tabiiy_kalit(s) funksiyasini yozing:

python
sorted(["fayl10", "fayl9", "fayl2"], key=tabiiy_kalit)
# ['fayl2', 'fayl9', 'fayl10']

Qo'shimcha: registrsiz va o'zbek apostroflari bilan ishlashi kerak.

Vazifa 5: Saralash tahlilchisi

Dastur yozing:

  1. Turli ma'lumot turlarida (saralangan, teskari, tasodifiy, deyarli) tezlik
  2. key bilan va key siz farq
  3. lambda vs itemgetter farq
  4. Natijani jadval + █ grafik ko'rinishida

Vazifa 6: Jadval sinfi

Jadval sinfini yozing:

  1. sarala(ustun, kamayish=False) — barqarorlik bilan
  2. filtr(shart) — filtrlash
  3. guruhla(ustun) — groupby bilan
  4. eng_kattalar(ustun, k) — heapq bilan
  5. chiqar() — chiroyli jadval
  6. Zanjirlash: j.filtr(...).sarala(...).chiqar()

Vazifa 7: O'ylash

Nega Python barqaror saralashni kafolatlaydi, garchi barqarorlik biroz qo'shimcha xotira talab qilsa ham?

Javob

Uch sabab.

1. Ko'p mezonli saralash oddiy bo'ladi.

Barqarorliksiz har safar to'liq tuple kalit yozish kerak:

python
# Barqarorliksiz — bitta murakkab kalit
sorted(x, key=lambda v: (v.a, -v.b, v.c.casefold(), ...))

# Barqarorlik bilan — sodda qadamlar
x.sort(key=attrgetter("c"))
x.sort(key=attrgetter("b"), reverse=True)
x.sort(key=attrgetter("a"))

Ikkinchisi — turli yo'nalishlar bo'lganda yagona oson yechim. Satr uchun "minus" yo'q.

2. Foydalanuvchi interfeysi kutilgandek ishlaydi.

Jadvalda foydalanuvchi "Ball" ustunini bosadi, keyin "Guruh" ni bosadi. Barqarorlik tufayli natija — guruh bo'yicha, ichida ball bo'yicha. Bu — foydalanuvchi kutgan narsa, va u hech qanday qo'shimcha kodsiz ishlaydi.

Barqarorliksiz dasturchi saralash tarixini saqlab, murakkab kalit qurishi kerak edi.

3. Natija takrorlanuvchi (deterministik).

Barqarorliksiz bir xil kirish turli ishga tushirishlarda turli natija berishi mumkin (masalan, PYTHONHASHSEED yoki amalga oshirish tafsilotlariga qarab). Bu:

  • Testlarni "flaky" qiladi
  • Diff'larni shovqinli qiladi
  • Xatolarni takrorlashni qiyinlashtiradi

Narxi qancha?

Timsort baribir merge sort asosida ishlaydi va O(n) qo'shimcha xotira talab qiladi. Barqarorlik — bu dizaynda bepul keladi. Barqaror bo'lmagan quicksort tezroq bo'lishi mumkin edi, lekin:

  • Eng yomon holatda O(n²)
  • Adaptiv emas (qisman saralangan ma'lumotda tez emas)
  • Barqaror emas

Tim Peters bularning barchasini o'lchagan va Timsortni tanlagan. Java, Android, Swift, Rust ham keyinchalik shu tanlovni takrorlagan.

Xulosa: barqarorlik — "yaxshi bo'lardi" emas, balki API ning bir qismi. U ko'p mezonli saralashni Pythondagi eng oddiy amallardan biriga aylantirdi.

Nimani mustahkamlaydi: 2.2, 2.3, 2.4, 2.6, 2.8-bo'limlar.


Xulosa

Bu darsda saralashni chuqur o'rgandik.

Eng muhim uch fikr:

  1. key — saralashning yuragi. U har element uchun bir marta chaqiriladi va natijasi bo'yicha saralanadi. Tuple qaytarsa — ko'p mezonli saralash bo'ladi. operator.itemgetter / attrgetter — lambda dan tezroq.

  2. Barqarorlik kafolatlangan. Teng kalitlar asl tartibini saqlaydi. Bu — ketma-ket saralash orqali turli yo'nalishdagi ko'p mezonli saralashni mumkin qiladi: eng kam muhim mezondan boshlang.

  3. reverse=True, [::-1] emas. Birinchisi barqarorlikni saqlaydi, ikkinchisi buzadi. Va sort() None qaytaradi — sorted() yangi ro'yxat.

Keyingi darsda ro'yxat va xotira ni ko'ramiz: sys.getsizeof, o'sish strategiyasi, array moduli va qachon list o'rniga boshqa tuzilma kerakligi.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
6.3-dars: Ro'yxatni saralash — sort va sorted — IlmHamroh