IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur5/65-dars23 daqiqa
Mundarija (34)

JavaScript sort metodi: massivni saralash va taqqoslash funksiyasi

Qisqacha: sort massivni joyida saralaydi. Taqqoslash funksiyasisiz u har elementni satrga aylantirib solishtiradi, shuning uchun [10, 9, 1].sort() — [1, 10, 9]. Sonlar uchun funksiya bering: narxlar.sort((a, b) => a - b). Matnlar uchun — a.localeCompare(b, "uz"). Esda tuting: sort asl massivni o'zgartiradi.

Bu darsda

  • Nega sort sonlarni "noto'g'ri" tartiblashini tushuntira olasiz.
  • Taqqoslash funksiyasini ((a, b) => a - b) yozib, sonlarni o'sish va kamayish tartibida saralay olasiz.
  • Obyektlarni bir yoki bir necha kalit bo'yicha saralay olasiz va barqaror saralash nima ekanini bilasiz.
  • localeCompare va Intl.Collator bilan o'zbekcha so'zlarni saralay olasiz — va brauzerdagi tuzog'ini bilasiz.
  • vazifalar da bajarilmagan vazifalarni ro'yxat tepasiga chiqarasiz.

Oldin bilishingiz kerak: Callback imzosi va HOF metodlari, reduce, Taqqoslash operatorlari, Element qo'shish va olib tashlash, Havola semantikasi.

1. Nega bu kerak?

Jasur aka «Bahor» devoridagi menyu taxtasini yangilamoqchi: taomlar arzonidan qimmatiga qarab tursin. Narxlar massivda bor, massivda esa sort ("saralamoq") metodi bor. Sinab ko'ramiz:

js
const narxlar = [35000, 28000, 5000, 30000];
narxlar.sort();

console.log(narxlar); // [ 28000, 30000, 35000, 5000 ]

Ko'k choy (5 000 so'm) eng qimmat bo'lib qoldi! Xato xabari yo'q, dastur ishladi. Lekin natija noto'g'ri.

Taqqoslash operatorlari darsida bunga o'xshash tuzoqni ko'rgan edik: "10" < "9" — true. O'sha darsda yana bir va'da bergan edik: o'zbekcha so'zlarni alifbo bo'yicha to'g'ri saralash usulini shu darsda ko'rsatamiz. Bugun ikkala masalani ham hal qilamiz.

Hayotdan o'xshatish. Jismoniy tarbiya o'qituvchisi sinfni bo'y bo'yicha safga tizadi. U bir vaqtda faqat ikki o'quvchiga qaraydi: "Ali, Jasur — qaysi biringiz balandroq?" Javobga qarab ularning o'rnini almashtiradi yoki joyida qoldiradi. Kimni kim bilan solishtirishni o'qituvchi o'zi hal qiladi. Unga faqat qoida kerak: "balandi oldinda". sort — o'sha o'qituvchi, qoida esa — siz beradigan taqqoslash funksiyasi.

Reja: avval sort qoidasiz nima qilishini va nega shunday ekanini ko'ramiz. Keyin taqqoslash funksiyasi, uning ichidagi sikl va obyektlarni saralash. So'ng o'zbek harflari — o', g', sh, ch — va brauzerdagi kutilmagan farq. Oxirida sort ning eng xavfli xususiyati — asl massivni o'zgartirishi — va vazifalar dagi yangi imkoniyat.

2. Standart sort: hamma narsa satr

2.1 Satrlarda ishlaydi

Kichik harfli so'zlarda sort kutilgandek ishlaydi:

js
const taomlar = ["manti", "osh", "lag'mon"];
taomlar.sort();

console.log(taomlar); // [ "lag'mon", 'manti', 'osh' ]

l, m, o — alifbo tartibida. Shuning uchun ko'pchilik sort ni "alifbo bo'yicha saralaydi" deb o'ylaydi. Aslida u boshqa ish qiladi — sonlarda bu darhol ko'rinadi.

2.2 Sonlarda — tuzoq

js
const sonlar = [10, 9, 1];

console.log(sonlar.sort()); // [ 1, 10, 9 ]

10 — 9 dan oldin. Keyingi tugmasini bosib, sort ichida nima bo'lganini kuzating:

Lug'atdagi so'zlar kabi: "olma" "o'rik" dan oldin, chunki birinchi farq qilgan harf hal qiladi, so'zning uzunligi emas. sort sonlarni ham xuddi so'z kabi o'qidi: "10" ning birinchi belgisi "1", u "9" dan kichik — tamom.

2.3 Nega shunday qilingan?

Qoida standartda shunday yozilgan: taqqoslash funksiyasi berilmasa, sort elementlarni satrga aylantiradi va belgi kodlari bo'yicha solishtiradi. Sabab — massivda har xil turdagi qiymat bo'lishi mumkin: son, satr, null, obyekt. Ularning hammasini satrga aylantirish mumkin, demak satr tartibi — hamma uchun ishlaydigan yagona "umumiy" tartib. To'g'ri, lekin sonlar uchun foydasiz.

Bu qoidadan yana ikki g'alati natija chiqadi:

js
const mevalar = ["olma", "Behi", "anor", undefined, "zira"];

console.log(mevalar.sort());
// [ 'Behi', 'anor', 'olma', 'zira', undefined ]
  • Katta harf oldinda. B ning kodi (66) har qanday kichik harfnikidan (97–122) kichik. Shuning uchun "Behi" "anor" dan oldin turdi.
  • undefined doim oxirida. sort uni umuman solishtirmaydi — to'g'ridan-to'g'ri oxiriga qo'yadi.

Tekshirib ko'ring: [2, 100, 30].sort() nima qaytaradi?

Javob

[ 100, 2, 30 ]. Satr sifatida: "100", "2", "30". Birinchi belgilar — 1, 2, 3. Qolgan belgilar ahamiyatga ega emas: "100" birinchi belgisi tufayli eng oldinda.

3. Taqqoslash funksiyasi

3.1 (a, b) => a - b

Sonlarni son sifatida saralash uchun sort ga qoida beramiz:

js
const narxlar = [35000, 28000, 5000, 30000];
narxlar.sort((a, b) => a - b);

console.log(narxlar); // [ 5000, 28000, 30000, 35000 ]

Endi choy — eng arzon, osh — eng qimmat. Jasur akaning taxtasi tayyor.

Taqqoslash funksiyasi (comparator) — ikki elementni olib, ulardan qaysi biri oldin turishini son bilan aytadigan callback. Inglizcha "compare" — "solishtirmoq". Uning imzosi boshqa HOF metodlarnikidan farq qiladi: (element, index, array) emas, (a, b) — ikki element. Callback imzosi darsida shu istisnoni aytib o'tgan edik.

3.2 Qaytgan son nimani bildiradi

sort callback qaytargan sonning faqat ishorasiga qaraydi:

Callback qaytardi Ma'nosi
manfiy son (-1, -23000) a oldin turadi
musbat son (1, 30000) b oldin turadi
0 teng — o'zaro tartibi o'zgarmaydi

Sxemada xuddi shu qoida — o'qituvchining savoli va uchta javob:

flowchart TD
  S{"taqqosla(a, b) nima qaytardi?"}
  S -- "manfiy" --> A["a oldin, b keyin"]
  S -- "musbat" --> B["b oldin, a keyin"]
  S -- "0" --> C["teng: o'z joyida qoladi"]

a - b nega aynan shu qoidaga mos keladi? a kichik bo'lsa, ayirma manfiy — a oldin. a katta bo'lsa — musbat, b oldin. Teng bo'lsa — 0. Natija: kichigi oldinda, ya'ni o'sish tartibi.

Kamayish tartibi uchun ayirmani teskari oling:

js
const ballar = [78, 100, 9, 85];
ballar.sort((a, b) => b - a);

console.log(ballar); // [ 100, 85, 78, 9 ]

O'zingiz to'ldiring. Callback (4, 7) bilan chaqirildi. a - b qaytaradi [:-3], demak oldin turadi [:4]. b - a bilan esa oldin turadi .

3.3 sort ichidan

Oldingi darslarda metodning "ichini" o'zimiz yozib chiqqan edik. Saralashning eng sodda usullaridan biri — qo'shib-joylash (insertion). Karta o'ynayotgan odam qo'lidagi kartalarni shunday tartiblaydi: har yangi kartani chapdagi tartiblangan kartalar orasidan o'z joyiga suqib qo'yadi.

js
function saralash(massiv, taqqosla) {
  for (let i = 1; i < massiv.length; i++) {
    const joriy = massiv[i];
    let j = i - 1;
    while (j >= 0 && taqqosla(massiv[j], joriy) > 0) {
      massiv[j + 1] = massiv[j]; // kattasini o'ngga sur
      j--;
    }
    massiv[j + 1] = joriy; // bo'shagan joyga qo'y
  }
  return massiv;
}

const narxlar = [35000, 28000, 5000, 30000];
console.log(saralash(narxlar, (a, b) => a - b));
// [ 5000, 28000, 30000, 35000 ]

Uchta narsaga qarang:

  1. Callback faqat savolga javob beradi. taqqosla(massiv[j], joriy) > 0 — "chapdagisi keyin turishi kerakmi?". Kimni kim bilan solishtirish va elementlarni qayerga surishni sikl hal qiladi.
  2. Massiv joyida o'zgaradi. Yangi massiv yaratilmadi — massiv[j + 1] = ... aslining ichiga yozadi. Haqiqiy sort ham shunday; bu xususiyatning narxini «sort asl massivni o'zgartiradi» bo'limida ko'ramiz.
  3. Qaytgani — o'sha massiv. return massiv yangi nusxa emas.

Haqiqiy sort ichida murakkabroq algoritm ishlaydi. Chrome va Node'dagi V8 dvigateli TimSort ni ishlatadi: u qisqa bo'laklarni xuddi shunday qo'shib-joylab saralaydi, keyin bo'laklarni birlashtiradi. Saralash algoritmlarini Oddiy saralashlar va JS sort ichidan darslarida batafsil o'rganamiz — hozir bilish shart emas.

3.4 Kim kim bilan solishtiriladi?

Callback ichiga console.log qo'yib, sort uni qanday chaqirishini ko'raylik:

js
const narxlar = [30000, 5000, 28000];

narxlar.sort((a, b) => {
  console.log(`a=${a} b=${b}`);
  return a - b;
});

Konsolda:

text
a=5000 b=30000
a=28000 b=5000
a=28000 b=30000
a=28000 b=5000

Ikki narsa ko'rinadi. Birinchisi: a har doim massivdagi birinchi element emas — Node bu yerda keyingi elementni a qilib berdi. Ikkinchisi: bir juftlik ikki marta solishtirildi. Bu tartib dvigatelga (Node va Chrome'da — V8) bog'liq, JavaScript standarti uni belgilamaydi. Shuning uchun callback'dan faqat bitta narsa talab qilinadi: istalgan ikki element uchun izchil javob bersin. (5000, 30000) ga "5000 oldin" degan bo'lsa, (30000, 5000) ga ham "5000 oldin" desin.

Tekshirib ko'ring: [4, 2, 9].sort((a, b) => b - a) nima qaytaradi? Callback (2, 9) bilan chaqirilsa, nima javob beradi?

Javob

[ 9, 4, 2 ] — kamayish tartibi. (2, 9) uchun 9 - 2 = 7 — musbat, demak b (ya'ni 9) oldin turadi. Kattasi oldinda — kamayish.

4. Obyektlarni saralash

4.1 Bitta kalit bo'yicha

Real ma'lumot ko'pincha obyektlar massivi. Taqqoslash funksiyasi butun obyektlarni oladi — qaysi maydonni solishtirishni o'zimiz tanlaymiz:

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Ko'k choy", narx: 5000 },
  { nom: "Lag'mon", narx: 28000 },
];

menyu.sort((a, b) => a.narx - b.narx);

const nomlar = menyu.map((t) => t.nom);
console.log(nomlar); // [ "Ko'k choy", "Lag'mon", 'Osh' ]

a va b — taomlar, ayiriladigani esa ularning narx lari. Natijani tekshirish uchun faqat nomlarni map bilan oldik.

4.2 Bir necha kalit bo'yicha

Lag'mon va manti narxi bir xil bo'lsa-chi? Jasur aka aytadi: "narx teng bo'lsa — nomi bo'yicha". Bu ikki pog'onali qoida:

js
const menyu = [
  { nom: "Manti", narx: 30000 },
  { nom: "Osh", narx: 35000 },
  { nom: "Lag'mon", narx: 30000 },
];

menyu.sort((a, b) => a.narx - b.narx || a.nom.localeCompare(b.nom));
console.log(menyu.map((t) => t.nom)); // [ "Lag'mon", 'Manti', 'Osh' ]

Butun sir || operatorida (Mantiqiy operatorlar):

  • Narxlar har xil bo'lsa, a.narx - b.narx — nolmas son, ya'ni truthy. || uni qaytaradi, o'ng tomoni hisoblanmaydi ham.
  • Narxlar teng bo'lsa, ayirma 0 — falsy. Shunda || o'ng tomonga o'tadi va nomlarni solishtiradi.

localeCompare — matnlarni solishtiradigan metod, uni «Matnni to'g'ri saralash» bo'limida batafsil ko'ramiz. Hozircha bilib qo'ying: u ham manfiy, musbat yoki 0 qaytaradi — xuddi taqqoslash funksiyasidek. Uchinchi kalit kerak bo'lsa, yana bir || qo'shiladi.

4.3 Barqaror saralash

Otabekning buyurtmalar ro'yxati kelish vaqti bo'yicha tartiblangan. Oshxona ekranida esa avval tayyorlanayotganlar, keyin tayyorlari turishi kerak:

js
const buyurtmalar = [
  { stol: 3, tayyor: true },
  { stol: 7, tayyor: false },
  { stol: 1, tayyor: true },
  { stol: 5, tayyor: false },
];

buyurtmalar.sort((a, b) => Number(a.tayyor) - Number(b.tayyor));
console.log(buyurtmalar.map((b) => b.stol)); // [ 7, 5, 3, 1 ]

Number(false) — 0, Number(true) — 1 (Turlarni aniq o'zgartirish). Ayirma bo'yicha false lilar oldinga chiqadi.

Endi natijaga diqqat bilan qarang. Tayyorlanayotganlar orasida 7-stol 5-stoldan oldin — xuddi asl ro'yxatdagidek. Tayyorlar orasida ham 3-stol 1-stoldan oldin. Taqqoslash funksiyasi ular uchun 0 qaytardi va sort ularning o'zaro tartibini saqladi.

Barqaror saralash (stable sort) — teng elementlarning asl tartibi saqlanadigan saralash. Bu 2019-yildan (ES2019) standartda kafolatlangan. Undan oldin ba'zi brauzerlar teng elementlarni aralashtirib yuborishi mumkin edi — Chrome barqaror saralashga 2018-yilda, 70-versiyada o'tgan. Bugun barcha zamonaviy brauzer va Node'da shunday.

Barqarorlik amalda ikki kalitli saralashni ham osonlashtiradi: ro'yxat allaqachon vaqt bo'yicha tartiblangan bo'lsa, faqat holat bo'yicha saralash kifoya — vaqt tartibi guruhlar ichida o'zi saqlanadi.

Tekshirib ko'ring: Talabalar ro'yxati ism bo'yicha tartiblangan. Uni ball bo'yicha (a, b) => b.ball - a.ball bilan saralasak, balli bir xil ikki talaba qaysi tartibda turadi?

Javob

Ism tartibida — xuddi saralashdan oldingidek. Ballari teng — callback 0 qaytaradi. Barqaror saralash teng elementlarning asl tartibini o'zgartirmaydi, asl tartib esa ism bo'yicha edi.

5. Matnni to'g'ri saralash: localeCompare

5.1 Katta-kichik harf

Standart sort "Behi" ni "anor" dan oldin qo'ydi — katta harf kodi kichik. Odam uchun esa bu xato: lug'atda "anor" birinchi. Matnni til qoidalari bo'yicha solishtirish uchun satrlarning localeCompare metodi bor:

js
console.log("anor".localeCompare("behi")); // -1
console.log("behi".localeCompare("anor")); // 1
console.log("osh".localeCompare("osh")); // 0

Javoblari — taqqoslash funksiyasiga kerakli uchta qiymat. Demak uni to'g'ridan-to'g'ri callback ichida ishlatsa bo'ladi:

js
const mevalar = ["olma", "Behi", "anor"];
mevalar.sort((a, b) => a.localeCompare(b));

console.log(mevalar); // [ 'anor', 'Behi', 'olma' ]

"Behi" endi o'z joyida — "anor" va "olma" orasida. localeCompare katta va kichik b ni bitta harf deb biladi.

5.2 O'zbek harflari

O'zbek lotin alifbosi z bilan tugamaydi: undan keyin o', g', sh, ch keladi. Standart sort va tilsiz localeCompare buni bilmaydi:

js
const sozlar = ["zira", "o'rik", "olma", "choy", "g'oza", "gilos"];

console.log(sozlar.sort((a, b) => a.localeCompare(b)).join(", "));
// choy, g'oza, gilos, o'rik, olma, zira

"choy" — boshida, "o'rik" — "olma" dan oldin. Bu ingliz tilidagi tartib: apostrof harflardan oldin turadi, ch esa oddiy c va h.

localeCompare ning ikkinchi argumenti — til kodi. O'zbek tili uchun — "uz":

js
const sozlar = ["zira", "o'rik", "olma", "choy", "g'oza", "gilos"];

const tartib = sozlar.sort((a, b) => a.localeCompare(b, "uz"));
console.log(tartib.join(", "));
// gilos, olma, zira, o'rik, g'oza, choy

Endi tartib o'zbek alifbosidagidek: z dan keyin o', keyin g', oxirida ch. Bu natija Node 24 da olingan — unda xalqaro tillar ma'lumoti to'liq. Node 24 apostrofning uch xil yozilishini (', ‘, ʻ) ham bir xil tushunadi. Faqat ng alohida harf deb hisoblanmaydi — u n va g sifatida saralanadi.

5.3 Brauzerda boshqacha!

Xuddi shu kodni Chrome konsolida ishga tushiring. Biz Chrome 154 da sinadik:

js
const sozlar = ["zira", "o'rik", "olma", "choy", "g'oza", "gilos"];
sozlar.sort((a, b) => a.localeCompare(b, "uz"));
console.log(sozlar.join(", "));

Konsolda:

text
choy, g'oza, gilos, o'rik, olma, zira

"uz" yozilgan bo'lsa ham — inglizcha tartib. Sabab: brauzerlar hajmni tejash uchun hamma tillarning saralash qoidalarini o'zi bilan olib yurmaydi. Chrome'da o'zbek tili qoidalari yo'q, u jimgina o'zining standart tiliga qaytadi.

Buni tekshirish mumkin. JavaScript'da Intl degan tayyor obyekt bor — tilga bog'liq ishlar (sana, son, matn tartibi) uchun vositalar to'plami. Uning Intl.Collator qismi matnlarni solishtiradi:

js
console.log(Intl.Collator.supportedLocalesOf(["uz", "ru", "tr"]));
// Chrome 154: [ 'ru', 'tr' ]   Node 24: [ 'uz', 'ru', 'tr' ]

supportedLocalesOf — "shu tillardan qaysilarini qo'llaysan?" degan savol. Chrome rus va turk tillarini qo'llaydi, o'zbekchani — yo'q. Node esa uchalasini ham.

Bundan amaliy xulosa: o'zbekcha alifbo tartibi muhim bo'lsa (masalan, mijozlar ro'yxati), saralashni serverda — Node'da — qiling yoki o'zingiz alifbo jadvali bilan taqqoslash funksiyasini yozing. Ikkinchisini 3-mashqda qilamiz.

5.4 Intl.Collator — tayyor taqqoslash funksiyasi

localeCompare har chaqiruvda til qoidalarini qaytadan tayyorlaydi. Ming so'zni saralaganda bu ish minglab marta takrorlanadi. Buning o'rniga qoidalarni bir marta tayyorlab qo'yish mumkin:

js
const uzTartib = new Intl.Collator("uz", { numeric: true });
const stollar = ["10-stol", "9-stol", "1-stol"];

console.log(stollar.sort(uzTartib.compare));
// [ '1-stol', '9-stol', '10-stol' ]

new Intl.Collator("uz", ...) — o'zbek tili qoidalari bo'yicha solishtiruvchi yasaydi. new so'zini Konstruktor funksiya darsida o'rganamiz — hozir "yangisini yasa" deb o'qing. Uning compare xususiyati — tayyor taqqoslash funksiyasi, uni qavssiz beramiz.

{ numeric: true } — matn ichidagi sonlarni son sifatida solishtir. Usiz "10-stol" "9-stol" dan oldin turardi — o'sha "10" < "9" tuzog'i. Intl.Collator ning boshqa sozlamalarini Intl.Collator va Intl.Segmenter darsida ko'ramiz.

Tekshirib ko'ring: Telegram bot foydalanuvchilar ismini o'zbek alifbosi bo'yicha saralab yuborishi kerak. Bot serverda, Node 24 da ishlaydi. Qaysi taqqoslash funksiyasi to'g'ri: (a, b) => a.localeCompare(b) yoki (a, b) => a.localeCompare(b, "uz")?

Javob

Ikkinchisi. Til kodisiz localeCompare muhitning standart tilini oladi — ko'pincha ingliz. Shunda "O'tkir" "Olim" dan oldin, "Shuhrat" esa "Sardor" dan oldin chiqadi. Node 24 da "uz" qoidalari bor, shuning uchun serverda to'g'ri ishlaydi. Shu kod brauzerda ishlasa — Chrome'da yana inglizcha tartib bo'lardi.

6. sort asl massivni o'zgartiradi

«sort ichidan» bo'limida ko'rdik: saralash massivning o'zida bo'ladi. Element qo'shish va olib tashlash darsidagi atama bilan — sort mutatsiya qiladi. Bundan tashqari, u yangi massiv emas, o'sha massivni qaytaradi:

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Ko'k choy", narx: 5000 },
];

const arzondan = menyu.sort((a, b) => a.narx - b.narx);

console.log(menyu[0].nom); // Ko'k choy
console.log(arzondan === menyu); // true

Otabek faqat "arzondan qimmatga" ko'rinishini olmoqchi edi. Lekin asl menyu ham qayta tartiblandi: arzondan va menyu — bitta massiv (Havola semantikasi). Menyuning boshqa joyda ishlatiladigan tartibi (masalan, oshpaz kelishilgan tartib) yo'qoldi.

To'g'ri yo'l — avval nusxa, keyin saralash:

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Ko'k choy", narx: 5000 },
];

const arzondan = [...menyu].sort((a, b) => a.narx - b.narx);

console.log(menyu[0].nom); // Osh
console.log(arzondan[0].nom); // Ko'k choy

[...menyu] — yangi massiv (Spread), sort o'shani tartibladi. filter, map va slice ham yangi massiv qaytaradi — ularning natijasini saralash xavfsiz: menyu.filter(...).sort(...).

Diqqat: sort — HOF metodlar orasida kam uchraydigan mutatsiya qiluvchi metod. map, filter, reduce asl massivga tegmaydi; sort esa tegadi. Bu farqni unutish — real loyihalardagi eng ko'p uchraydigan xatolardan. 2023-yilda JavaScript'ga buning uchun alohida metod qo'shildi: toSorted — asl massivga tegmay, saralangan nusxa qaytaradi. Uni keyingi darsda, Nusxa bilan o'zgartirish da o'rganamiz.

Tekshirib ko'ring: const ballar = [5, 3, 9]; const eng = ballar.sort((a, b) => b - a)[0]; dan keyin ballar[0] nechaga teng?

Javob

9. Sizga faqat eng katta son kerak edi, lekin sort ballar ning o'zini kamayish tartibiga keltirdi. Eng kattasini topish uchun saralash shart ham emas: Math.max(...ballar) (Math obyekti).

7. Ko'p uchraydigan xatolar

7.1 Sonlarni taqqoslash funksiyasisiz saralash

«Nega bu kerak?» bo'limidagi menyu: [35000, 28000, 5000, 30000].sort() — [ 28000, 30000, 35000, 5000 ]. Xato xabari yo'q. Kichik sinovda ([3, 1, 2]) hammasi to'g'ri chiqadi — xato faqat xonalar soni har xil bo'lganda ko'rinadi. Tuzatish: sonlar uchun doim (a, b) => a - b.

7.2 Taqqoslash funksiyasi true/false qaytaradi

Eski maqolalarda ba'zan shunday yozilgan:

js
const sonlar = [5, 1, 4, 2, 3];
sonlar.sort((a, b) => a > b);

console.log(sonlar); // [ 5, 1, 4, 2, 3 ]

Hech narsa o'zgarmadi! a > b — true yoki false, son sifatida 1 yoki 0. Manfiy javob hech qachon chiqmaydi — sort hech qachon "a oldin" degan javobni eshitmaydi. Ba'zi eski dvigatellarda bu tasodifan ishlagan, shuning uchun hali ham internetda uchraydi. Tuzatish: (a, b) => a - b.

7.3 Satrlarni ayirish

js
const ismlar = ["Zafar", "Ali", "Malika"];
ismlar.sort((a, b) => a - b);

console.log(ismlar); // [ 'Zafar', 'Ali', 'Malika' ]

Yana o'zgarmadi. "Zafar" - "Ali" — NaN (Yashirin coercion). sort NaN ni 0 deb qabul qiladi — "teng", tartibni o'zgartirma. Tuzatish: satrlar uchun a.localeCompare(b).

7.4 Saralangan nusxa o'rniga aslini o'zgartirish

«sort asl massivni o'zgartiradi» bo'limidagi menyu: const arzondan = menyu.sort(...) — menyu ning ham tartibi buzildi. Funksiya ichida bo'lsa, xato yanada yashirin: funksiya argument sifatida olgan massivni chaqiruvchi bilmagan holda o'zgartiradi. Tuzatish: [...menyu].sort(...) yoki keyingi darsdagi toSorted.

8. Mashqlar

1-mashq (oson): Imtihon natijalari

Ballarni eng yuqorisidan boshlab saralang. Avval taqqoslash funksiyasisiz sinab ko'ring va natija nega noto'g'ri ekanini tushuntiring.

js
const ballar = [78, 100, 9, 85];
Yechim
js
const ballar = [78, 100, 9, 85];

console.log([...ballar].sort()); // [ 100, 78, 85, 9 ]
console.log([...ballar].sort((a, b) => b - a)); // [ 100, 85, 78, 9 ]

Birinchisida 100 tasodifan oldinda: satr sifatida "1" boshqalardan kichik. Lekin 9 oxirida, 85 esa 78 dan keyin — bu kamayish ham, o'sish ham emas. Ikkinchisi — kamayish: b - a. Har gal [...ballar] nusxa oldik, shuning uchun asl ballar o'zgarmadi.

2-mashq (o'rta): Taksi haydovchilari

Taksi ilovasi haydovchilarni reyting bo'yicha ko'rsatadi: avval reytingi yuqorilari. Reytingi teng bo'lsa — ismi o'zbek alifbosi bo'yicha. Kod Node'da ishlaydi.

Ishora: ikki pog'onali qoida — «Bir necha kalit bo'yicha» bo'limidagi ||. Ismlar uchun — localeCompare(…, "uz").

js
const haydovchilar = [
  { ism: "Shuhrat", reyting: 4.9 },
  { ism: "Olim", reyting: 4.7 },
  { ism: "O'tkir", reyting: 4.9 },
  { ism: "Zafar", reyting: 4.9 },
  { ism: "Akmal", reyting: 4.7 },
];
Yechim
js
const haydovchilar = [
  { ism: "Shuhrat", reyting: 4.9 },
  { ism: "Olim", reyting: 4.7 },
  { ism: "O'tkir", reyting: 4.9 },
  { ism: "Zafar", reyting: 4.9 },
  { ism: "Akmal", reyting: 4.7 },
];

haydovchilar.sort(
  (a, b) => b.reyting - a.reyting || a.ism.localeCompare(b.ism, "uz"),
);

for (const h of haydovchilar) {
  console.log(`${h.reyting} ${h.ism}`);
}

Konsolda:

text
4.9 Zafar
4.9 O'tkir
4.9 Shuhrat
4.7 Akmal
4.7 Olim

4.9 lik uchtasi o'zbek alifbosi tartibida: z, o', sh. Tilsiz localeCompare bilan "O'tkir" birinchi chiqardi. Reytinglar kasr son, lekin b.reyting - a.reyting ishorasi to'g'ri — kasr xatosi tartibga ta'sir qilmaydi.

3-mashq (qiyin): Brauzer uchun o'zbekcha tartib

Chrome'da localeCompare(…, "uz") ishlamasligini ko'rdik. O'zingizning uzTaqqosla(a, b) funksiyangizni yozing. U kichik harfli so'zlarni o'zbek alifbosi bo'yicha solishtirsin:

  1. alifbo — 28 ta harf massivi: a b d e f g h i j k l m n o p q r s t u v x y z o' g' sh ch.
  2. harflar(soz) — so'zni harflarga bo'ladi. Navbatdagi ikki belgi (slice(i, i + 2)) alifboda bo'lsa (o', g', sh, ch), u bitta harf. Aks holda — bitta belgi.
  3. uzTaqqosla — ikki so'zning harflarini birinchisidan boshlab solishtiradi. Birinchi farq qilgan juftlikda harflarning alifbodagi o'rinlari ayirmasini (indexOf) qaytaradi. Farq topilmasa — qisqa so'z oldin.

Natijani Node'dagi localeCompare(…, "uz") bilan solishtiring.

Yechim
js
const alifbo = [
  "a", "b", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
  "n", "o", "p", "q", "r", "s", "t", "u", "v", "x", "y", "z",
  "o'", "g'", "sh", "ch",
];

function harflar(soz) {
  const natija = [];
  let i = 0;
  while (i < soz.length) {
    const juft = soz.slice(i, i + 2);
    if (alifbo.includes(juft)) {
      natija.push(juft);
      i += 2;
    } else {
      natija.push(soz[i]);
      i += 1;
    }
  }
  return natija;
}

function uzTaqqosla(a, b) {
  const x = harflar(a.toLowerCase());
  const y = harflar(b.toLowerCase());
  for (let i = 0; i < Math.min(x.length, y.length); i++) {
    const farq = alifbo.indexOf(x[i]) - alifbo.indexOf(y[i]);
    if (farq !== 0) {
      return farq;
    }
  }
  return x.length - y.length;
}

const sozlar = ["choy", "zira", "o'rik", "olma", "shaftoli", "g'oza"];
const bizniki = [...sozlar].sort(uzTaqqosla);
const node = [...sozlar].sort((a, b) => a.localeCompare(b, "uz"));

console.log(bizniki.join(", "));
console.log(node.join(", "));

Konsolda:

text
olma, zira, o'rik, g'oza, shaftoli, choy
olma, zira, o'rik, g'oza, shaftoli, choy

Ikki qator bir xil. harflar("shaftoli") — sh, a, f, t, o, l, i: yetti harf, sakkiz belgi. Farq topilmasa, qisqa so'z oldin: "osh" "oshxona" dan oldin. Bu funksiya brauzerda ham, Node'da ham bir xil ishlaydi — chunki u til ma'lumotiga emas, o'z jadvaliga tayanadi.

Chegaralari ham bor: alifbodan tashqari belgilar (ʻ, raqam, bo'sh joy) indexOf da -1 beradi va hammasidan oldin turadi. Apostrofning boshqa yozilishlarini oldindan ' ga almashtirish kerak. Real loyihada bunday jadvalni sinab ko'rilgan kutubxonadan olishadi yoki saralashni serverga topshirishadi.

4-mashq: Vazifalar qadami — bajarilmaganlar tepada

vazifalar ga birinchi yangi imkoniyat qo'shamiz. Hozir vazifalar qo'shilgan tartibida turadi: bajarilgani ham ro'yxat o'rtasida qolib ketadi. Talab:

  • Bajarilmagan vazifalar ro'yxat tepasida, bajarilganlar — pastda.
  • Har guruh ichida tartib o'zgarmasin — qo'shilgan tartibi.
  • holat.vazifalar ning o'zi saralanmasin — faqat ekranga chiqadigan ro'yxat.

Yangi branch: git switch -c feature/saralash. Bu safar feature/ — xulq o'zgaradi. reduce darsidan keyin korinadiganlar shunday:

js
function korinadiganlar() {
  return holat.vazifalar.filter(
    (vazifa) => mosmi(vazifa, holat.filtr),
  );
}

Ishora: «Barqaror saralash» bo'limidagi Number(a.tayyor) - Number(b.tayyor) ni eslang.

Yechim

"3. Hisoblovchi funksiyalar" bo'limiga, mosmi dan keyin — taqqoslash funksiyasi:

js
function faollarOldin(a, b) {
  return Number(a.bajarildi) - Number(b.bajarildi);
}

korinadiganlar — filtrdan keyin saralash:

js
function korinadiganlar() {
  return holat.vazifalar
    .filter((vazifa) => mosmi(vazifa, holat.filtr))
    .sort(faollarOldin);
}

Uchala talab qanday bajarildi:

  • Faollar tepada. Bajarilmagan — 0, bajarilgan — 1; ayirma bo'yicha 0 lar oldin.
  • Guruh ichida tartib saqlanadi. Bir guruhdagi ikki vazifa uchun javob 0 — barqaror saralash ularni qo'shilgan tartibida qoldiradi. id bo'yicha ikkinchi kalit kerak emas.
  • holat saralanmaydi. sort filter yasagan yangi massivni tartibladi. «Hammasi» filtrida ham filter yangi massiv qaytaradi — barcha elementlar o'tgan bo'lsa ham.

Nega holat.vazifalar.sort(...) emas? Node'da ikkala variantni bir xil harakatlar bilan solishtiring: 1-vazifani belgilaymiz, keyin qaytaramiz.

js
const vazifalar = [
  { id: 1, bajarildi: false },
  { id: 2, bajarildi: true },
  { id: 3, bajarildi: false },
];

function faollarOldin(a, b) {
  return Number(a.bajarildi) - Number(b.bajarildi);
}

const idlar = (royxat) => royxat.map((v) => v.id).join(",");
const tozasi = () => vazifalar.filter(() => true).sort(faollarOldin);
const buzadigani = () => vazifalar.sort(faollarOldin);

for (const korsat of [tozasi, buzadigani]) {
  vazifalar.sort((a, b) => a.id - b.id); // boshlang'ich holat
  vazifalar[0].bajarildi = false;
  const qadamlar = [idlar(korsat())];
  vazifalar.find((v) => v.id === 1).bajarildi = true;
  qadamlar.push(idlar(korsat()));
  vazifalar.find((v) => v.id === 1).bajarildi = false;
  qadamlar.push(idlar(korsat()));
  console.log(qadamlar.join("  →  "), "| holat:", idlar(vazifalar));
}

Konsolda:

text
1,3,2  →  3,1,2  →  1,3,2 | holat: 1,2,3
1,3,2  →  3,1,2  →  3,1,2 | holat: 3,1,2

Birinchi qator — bizning yechim: 1-vazifa qaytarilganda ro'yxat boshiga, o'z joyiga qaytdi. holat tartibi — 1,2,3, o'zgarmagan. Ikkinchi qator — holat.vazifalar.sort(...): qaytarilgan 1-vazifa endi 3-dan keyin qoldi. Har render holat ning o'zini qayta tartibladi va asl tartib yo'qoldi. Xato xabari yo'q — faqat foydalanuvchi "nega vazifam joyini o'zgartirdi?" deb hayron qoladi. (filter(() => true) bu yerda faqat "yangi massiv yasash" uchun — haqiqiy kodda uning o'rnida mosmi bilan filtr turadi.)

Brauzerda tekshiring:

  • Ochilganda: "Non olish", "Onamga qo'ng'iroq qilish", keyin chizilgan "DOM darsini takrorlash".
  • "Non olish" ni belgilang — u pastga tushadi, fokus uning katagida qoladi. Qaytaring — u yana birinchi o'rinda.
  • Yangi vazifa qo'shing — u faollarning oxirida, bajarilganlardan oldin paydo bo'ladi.
  • «Faol» va «Bajarilgan» filtrlari, tahrirlash, Esc, o'chirish — avvalgidek.

Belgilangan vazifa pastga tushganda fokus u bilan birga ketadi: fokusla uni id bo'yicha topadi, joyi bo'yicha emas. Klaviatura bilan ishlaydigan Bobur ham, NVDA ishlatadigan Malika ham fokusni yo'qotmaydi.

Commit va main ga qo'shish:

bash
git add assets/js/vazifalar.js
git commit -m "Ro'yxat: bajarilmagan vazifalar tepada"
git switch main
git merge feature/saralash
git branch -d feature/saralash
git push

9. Real ishda

  • Jadval ustunlari. Internet do'kondagi "narx bo'yicha", "yangilari avval", "reyting bo'yicha" tugmalari — har biri bitta taqqoslash funksiyasi. Admin paneldagi jadval sarlavhasini bosib saralash ham shunday.
  • Ismlar ro'yxati. Maktab jurnali, xodimlar ro'yxati, Telegram kontaktlari — alifbo tartibi. O'zbekcha tartib kerak bo'lsa, saralashni serverda localeCompare(…, "uz") yoki Intl.Collator bilan qiling — brauzerga ishonmang.
  • Ma'lumotlar bazasi. Katta ro'yxatlarni odatda baza saralab beradi (ORDER BY — SQL qismida). Lekin bazadan kelgan kichik ro'yxatni foydalanuvchi bosgan tugmaga qarab qayta saralash — brauzerdagi sort ning ishi.
  • Intervyu. "[10, 9, 1].sort() nima qaytaradi va nega?", "taqqoslash funksiyasi nima qaytarishi kerak?", "sort barqarormi?" — junior intervyusida tez-tez beriladi. Javoblar: [1, 10, 9] — satr tartibi; manfiy, musbat yoki 0; ha, ES2019 dan.

Xulosa

  • sort taqqoslash funksiyasisiz elementlarni satrga aylantirib solishtiradi: [10, 9, 1] → [1, 10, 9].
  • Taqqoslash funksiyasi (a, b) oladi va son qaytaradi: manfiy — a oldin, musbat — b oldin, 0 — joyida. Sonlar: a - b (o'sish), b - a (kamayish).
  • Bir necha kalit — || bilan: a.narx - b.narx || a.nom.localeCompare(b.nom). Saralash barqaror: teng elementlar tartibi saqlanadi.
  • Matnlar — localeCompare(b, "uz") yoki new Intl.Collator("uz").compare. Node 24 o'zbek alifbosini biladi, Chrome — yo'q.
  • sort asl massivni o'zgartiradi va o'shani qaytaradi. Asli kerak bo'lsa — avval nusxa: [...massiv].sort(...).

Keyingi dars: Nusxa bilan o'zgartirish: toSorted, toReversed, toSpliced, with — asl massivga tegmasdan saralash, teskari aylantirish va o'zgartirishning zamonaviy usullari.

Manbalar

  • MDN: "Array.prototype.sort()", "String.prototype.localeCompare()", "Intl.Collator" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "Array.prototype.sort", "SortCompare" — tc39.es/ecma262
  • V8 blog: "Getting things sorted in V8" (2018) — v8.dev/blog/array-sort
  • web-features: "Stable array sort" — Baseline, Chrome 70 dan
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript sort metodi: massivni saralash va taqqoslash funksiyasi — IlmHamroh