Arrays Methods

...

Massiv metodlari

Massivlar ko'p metodlarni taqdim etadi. Ishni osonlashtirish uchun, ushbu bo'limda ular guruhlarga ajratilgan.

Elementlarni qo'shish/olish

Avval biz oxiridan yoki boshidan elementlarni qo'shish yoki olish metodlarini bilamiz:

  • arr.push(...items) – elementlarni oxiriga qo'shadi,
  • arr.pop() – oxiridan elementni olib tashlaydi,
  • arr.shift() – boshidan elementni olib tashlaydi,
  • arr.unshift(...items) – boshiga elementlarni qo'shadi.

Mana bir necha boshqalar.

splice

Massivdan elementni qanday olib tashlash mumkin?

Massivlar ob'ektlar bo'lgani uchun, delete ishlatishga harakat qilamiz:

let arr = ['Men', 'boraman', 'uyga'];
 
delete arr[1]; // "boraman"ni olib tashlaydi
 
alert(arr[1]); // undefined
 
// endi arr = ["Men", , "uyga"];
alert(arr.length); // 3

Element olib tashlandi, lekin massivning uzunligi hali ham 3 bo'lib qoladi. Bu tabiiy, chunki delete obj.key qiymatni faqat kalit bo'yicha olib tashlaydi. Bu ob'ektlar uchun yaxshi. Lekin massivlarda biz odatda qolgan elementlarning bo'sh joyni egallashini kutamiz. Biz endi qisqaroq massivga ega bo'lishimizni kutamiz.

Shuning uchun, maxsus metodlar ishlatilishi kerak.

arr.splice metodi massivlar uchun Shveytsariya armiyasi pichog'i sifatida ishlaydi. U hamma narsani bajarishi mumkin: elementlarni qo'shish, olib tashlash va almashtirish.

Sintaksisi:

arr.splice(start[, deleteCount, elem1, ..., elemN])

Bu arrni start indeksidan boshlab o'zgartiradi: deleteCount elementlarni olib tashlaydi va keyin elem1, ..., elemNni ularning o'rnida qo'shadi. Olib tashlangan elementlar massivini qaytaradi.

Bu metodni misollar bilan tushunish oson.

Boshlaymiz olib tashlashdan:

let arr = ['Men', "o'rganyapman", 'JavaScript'];
 
arr.splice(1, 1); // indeks 1dan 1 elementni olib tashlaydi
 
alert(arr); // ["Men", "JavaScript"]

Oshkor, to'g'rimi? Indeks 1dan boshlab 1 elementni olib tashladi.

Keyingi misolda, 3 elementni olib tashlab, ularni boshqa ikkita bilan almashtiramiz:

let arr = ['Men', "o'rganyapman", 'JavaScript', "to'g'ri", 'hozir'];
 
arr.splice(0, 3, 'Keling', 'raqsga');
 
alert(arr); // endi ["Keling", "raqsga", "to'g'ri", "hozir"]

splice olib tashlangan elementlarning massivini qaytaradi:

let arr = ['Men', "o'rganyapman", 'JavaScript', "to'g'ri", 'hozir'];
 
// 2 ta birinchi elementlarni olib tashlash
let removed = arr.splice(0, 2);
 
alert(removed); // "Men", "o'rganyapman" <-- olib tashlangan elementlarning massivi

splice elementlarni olib tashlamasdan qo'shish imkonini ham beradi. Buning uchun deleteCountni 0 ga qo'ying:

let arr = ['Men', "o'rganyapman", 'JavaScript'];
 
// indeks 2dan
// 0 ni olib tashlash
// keyin "murakkab" va "til"ni qo'shish
arr.splice(2, 0, 'murakkab', 'til');
 
alert(arr); // "Men", "o'rganyapman", "murakkab", "til", "JavaScript"

Salbiy indekslar ruxsat etiladi

Bu va boshqa massiv metodlarida salbiy indekslar ruxsat etiladi. Ular massivning oxiridan pozitsiyani belgilaydi, masalan:

let arr = [1, 2, 5];
 
// -1 indeksidan (oxirdan bir qadam)
arr.splice(-1, 0, 3, 4);
 
alert(arr); // 1,2,3,4,5

slice

arr.slice metodi arr.splicega qaraganda ancha oddiyroq.

Sintaksisi:

arr.slice([start], [end]);

Bu yangi massiv qaytaradi, unda startdan endgacha (lekin endni o'z ichiga olmaydi) bo'lgan barcha elementlarni nusxalaydi. Har ikkala start va end salbiy bo'lishi mumkin, bu holda oxirdan pozitsiya qabul qilinadi.

Bu satr metodiga o'xshaydi, ammo substratlar o'rniga submassivlar yaratadi.

Masalan:

let arr = ['t', 'e', 's', 't'];
 
alert(arr.slice(1, 3)); // e,s (1dan 3gacha nusxalaydi)
 
alert(arr.slice(-2)); // s,t (oxirdan -2 dan oxirigacha nusxalaydi)

Agar argumentlarsiz chaqirsak, arr.slice() arrning nusxasini yaratadi. Bu ko'pincha asliy massivi ta'sir qilmaydigan qo'shimcha transformatsiyalar uchun nusxa olish uchun ishlatiladi.

concat

arr.concat metodi yangi massiv yaratadi, unda boshqa massivlar va qo'shimcha elementlar mavjud bo'ladi.

Sintaksisi:

arr.concat(arg1, arg2...)

Har qanday sonli argumentlarni qabul qiladi – yoki massivlar yoki qiymatlar.

Natija, arrdan, keyin esa arg1, arg2 va hokazo elementlarni o'z ichiga olgan yangi massivdir.

Agar argument argN massiv bo'lsa, undagi barcha elementlar nusxalanadi. Aks holda, argument o'zi nusxalanadi.

Masalan:

let arr = [1, 2];
 
// arr va [3,4] dan massiv yaratish
alert(arr.concat([3, 4])); // 1,2,3,4
 
// arr va [3,4] va [5,6] dan massiv yaratish
alert(arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6
 
// arr va [3,4] dan massiv yaratish, keyin 5 va 6 qiymatlarni qo'shish
alert(arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6

Odatda, u faqat massivlardan elementlarni nusxalaydi. Boshqa ob'ektlar, hatto ular massivlarga o'xshasa ham, butunlay qo'shiladi:

let arr = [1, 2];
 
let arrayLike = {
  0: 'biror narsa',
  length: 1,
};
 
alert(arr.concat(arrayLike)); // 1,2,[object Object]

...Lekin agar massivga o'xshash ob'ekt Symbol.isConcatSpreadable xususiyatiga ega bo'lsa, unda u concat tomonidan massiv sifatida qabul qilinadi: uning elementlari qo'shiladi:

let arr = [1, 2];
 
let arrayLike = {
  0: 'biror narsa',
  1: 'boshqa',
  [Symbol.isConcatSpreadable]: true,
  length: 2,
};
 
alert(arr.concat(arrayLike)); // 1,2,biror narsa,boshqa

Iteratsiya: forEach

arr.forEach metodi har bir massiv elementi uchun funktsiyani bajarishga imkon beradi.

Sintaksisi:

arr.forEach(function (item, index, array) {
  // ... item bilan nima qilish kerak
});

Masalan, bu massivning har bir elementini ko'rsatadi:

// har bir element uchun alert chaqirish
['Bilbo', 'Gandalf', 'Nazgul'].forEach(alert);

Va bu kodning maqsadi, elementlarning maqsad massivdagi pozitsiyasini ko'rsatadi:

['Bilbo', 'Gandalf', 'Nazgul'].forEach((item, index, array) => {
  alert(`${item} indeksta ${index} ${array}`);
});

Funktsiyaning natijasi (agar mavjud bo'lsa) chiqariladi va e'tiborsiz qoldiriladi.

Massivni qidirish

Endi massivda qidirish metodlarini ko'rib chiqamiz.

indexOf/lastIndexOf

indexOf va lastIndexOf metodlari massivda biror elementning indeksini topadi.

Sintaksisi:

arr.indexOf(item[, fromIndex])
arr.lastIndexOf(item[, fromIndex])

indexOf birinchi ko'rinishning indeksini qaytaradi, lastIndexOf esa oxirgi ko'rinishning indeksini qaytaradi. Agar kerakli element mavjud bo'lmasa, -1 qaytariladi.

let arr = [1, 0, 1];
 
alert(arr.indexOf(1)); // 0 (birinchi ko'rinish)
alert(arr.indexOf(5)); // -1 (element mavjud emas)
 
alert(arr.lastIndexOf(1)); // 2 (oxirgi ko'rinish)

Ularning ikkalasi ham ikkinchi argument sifatida qidirishni boshlash uchun indeksni qabul qiladi.

includes

arr.includes metodi true yoki false qaytaradi, agar massiv elementni o'z ichiga olsa yoki bo'lmasa.

Sintaksisi:

arr.includes(valueToFind[, fromIndex])

Agar kerakli qiymat mavjud bo'lsa, true qaytariladi, aks holda false.

let arr = [1, 2, 3, 4, 5];
 
alert(arr.includes(3)); // true
alert(arr.includes(6)); // false

find/findIndex

arr.find metodi massivdagi birinchi elementni qaytaradi, u berilgan shartlarga mos keladi.

Sintaksisi:

arr.find(function (item, index, array) {
  // shart
});

find birinchi mos keladigan elementni qaytaradi yoki undefined agar element topilmasa.

Masalan:

let users = [
  { id: 1, name: 'Bilbo' },
  { id: 2, name: 'Gandalf' },
];
 
let user = users.find((user) => user.id == 1);
 
alert(user.name); // Bilbo

findIndex esa shartlarga mos keladigan elementning indeksini qaytaradi.

let users = [
  { id: 1, name: 'Bilbo' },
  { id: 2, name: 'Gandalf' },
];
 
let index = users.findIndex((user) => user.id == 1);
 
alert(index); // 0

sort

arr.sort metodi massiv elementlarini saralash uchun ishlatiladi.

Sintaksisi:

arr.sort([compareFunction]);

Agar compareFunction bo'lsa, elementlar shu funksiyaning natijalariga ko'ra saralanadi. Agar compareFunction bo'lmasa, sort elementlarni ko'rsatish tartibida (string sifatida) saralaydi.

Misol:

let arr = [1, 2, 15];
 
arr.sort();
 
alert(arr); // 1,15,2

Chunki sort metodining default tartibi string sifatida saralaydi. Bu tartibni o'zgartirish uchun maxsus solishtirish funksiyasini ishlatish kerak.

Masalan:

let arr = [1, 2, 15];
 
arr.sort((a, b) => a - b);
 
alert(arr); // 1,2,15

reverse

arr.reverse metodi massivning tartibini teskari qiladi.

Sintaksisi:

arr.reverse();

Masalan:

let arr = [1, 2, 3];
 
arr.reverse();
 
alert(arr); // 3,2,1

fill

arr.fill metodi massivdagi barcha elementlarni ma'lum qiymat bilan to'ldiradi.

Sintaksisi:

arr.fill(value[, start[, end]])

Massivning start indeksidan end indeksigacha (lekin endni o'z ichiga olmaydi) barcha elementlarni value qiymati bilan to'ldiradi.

Masalan:

let arr = [1, 2, 3, 4, 5];
 
arr.fill(0, 1, 4);
 
alert(arr); // 1,0,0,0,5

copyWithin

arr.copyWithin metodi massivdagi elementlarni boshqa joyga nusxalaydi.

Sintaksisi:

arr.copyWithin(target, start[, end])

target indeksiga start indeksidan boshlab end indeksigacha (lekin endni o'z ichiga olmaydi) barcha elementlarni nusxalaydi.

Misol:

let arr = [1, 2, 3, 4, 5];
 
arr.copyWithin(0, 3);
 
alert(arr); // 4,5,3,4,5

split va join

Haqiqiy hayotdan bir misol. Biz xabarlar ilovasini yozmoqdamiz, va foydalanuvchi vergul bilan ajratilgan qabul qiluvchilar ro‘yxatini kiritadi: John, Pete, Mary. Lekin biz uchun bu yagona satrdan ko‘ra nomlar array'i qulayroq bo‘lar edi. Buni qanday olish mumkin?

str.split(delim) metodi buni amalga oshiradi. U satrni berilgan ajratuvchi delim bo‘yicha array'ga ajratadi.

Quyidagi misolda biz vergul va bo‘shliq orqali ajratamiz:

let names = 'Bilbo, Gandalf, Nazgul';
 
let arr = names.split(', ');
 
for (let name of arr) {
  alert(`A message to ${name}.`); // Bilbo va boshqalarga xabar
}

split metodining ikkinchi ixtiyoriy sonli argumenti mavjud – array uzunligiga cheklov qo‘yish uchun. Agar berilsa, ortiqcha elementlar e'tiborga olinmaydi. Amaliyotda bu kamdan-kam qo‘llaniladi:

let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);
 
alert(arr); // Bilbo, Gandalf

Harflarga ajratish

Bo‘sh qiymat bilan split(s) chaqiruvi satrni harflarga ajratadi:

let str = 'test';
 
alert(str.split('')); // t,e,s,t

join

arr.join(glue) metodi esa split ga teskari amalni bajaradi. U arr elementlarini qayta stringga aylantiradi va ular orasiga glue qo‘yadi.

Misol uchun:

let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
 
let str = arr.join(';'); // array'ni ";" yordamida stringga aylantiramiz
 
alert(str); // Bilbo;Gandalf;Nazgul

reduce/reduceRight

Agar bizga array'ni aylantirib chiqish kerak bo‘lsa – forEach, for yoki for..ofdan foydalanishimiz mumkin.

Agar bizga har bir element uchun ma'lumotlarni qaytarish kerak bo‘lsa – mapdan foydalanishimiz mumkin.

arr.reduce va arr.reduceRight metodlari ham shu turkumga kiradi, lekin biroz murakkabroq. Ular array asosida bitta qiymatni hisoblash uchun ishlatiladi.

Sintaksisi:

let value = arr.reduce(
  function (accumulator, item, index, array) {
    // ...
  },
  [initial]
);

Funksiya barcha array elementlariga ketma-ket qo‘llanadi va oldingi qo‘ng‘iroq natijasini keyingisiga o‘tkazadi.

Argumentlar:

  • accumulator – oldingi funksiya chaqiruvining natijasi, birinchi chaqiruvda initial ga teng (agar initial berilgan bo‘lsa).
  • item – hozirgi array elementi.
  • index – uning pozitsiyasi.
  • array – array o‘zi.

Funksiya qo‘llanilganda, oldingi funksiya chaqiruvi natijasi keyingisiga birinchi argument sifatida o‘tadi. Shunday qilib, birinchi argument aslida barcha oldingi bajaruvlarning birlashtirilgan natijasini saqlaydigan to‘plovchi hisoblanadi. Va oxirida u reducening natijasiga aylanadi.

Misol yordamida buni osonroq tushunish mumkin.

Bu yerda biz array yig‘indisini bir qatorli kodda olamiz:

let arr = [1, 2, 3, 4, 5];
 
let result = arr.reduce((sum, current) => sum + current, 0);
 
alert(result); // 15

Funksiya chaqiruvida faqat 2 argument ishlatilgan, bu odatda yetarli.

Batafsil ko‘rib chiqamiz.

  • Birinchi chaqiruvda sum boshlang‘ich qiymat (ya'ni reduce oxirgi argumenti), 0 ga teng, va current birinchi array elementiga teng, ya'ni 1. Shunday qilib, funksiya natijasi 1.
  • Ikkinchi chaqiruvda, sum = 1, biz unga ikkinchi array elementini (2) qo‘shamiz va qaytaramiz.
  • Uchinchi chaqiruvda, sum = 3 va yana bir element qo‘shamiz, va hokazo…

Biz hisoblash jarayonini jadvalda ham ko‘rishimiz mumkin:

sumcurrentresult
1-chaqiruv01
2-chaqiruv13
3-chaqiruv36
4-chaqiruv610
5-chaqiruv1015

Shunday qilib, oldingi chaqiruvning natijasi keyingi chaqiruvning birinchi argumentiga aylanadi.

reduceRight xuddi shu tarzda ishlaydi, lekin o‘ngdan chapga qarab hisoblaydi.

Reduce image

Bu yerda biz oldingi chaqiruv natijasi keyingi chaqiruvning birinchi argumentiga aylanishini aniq ko'rishimiz mumkin.

Biz dastlabki qiymatni e'tiborsiz qoldirishimiz ham mumkin:

let arr = [1, 2, 3, 4, 5];
 
// dastlabki qiymat olib tashlandi (0 yo'q)
let result = arr.reduce((sum, current) => sum + current);
 
alert(result); // 15

Natija o'sha. Chunki agar dastlabki qiymat mavjud bo'lmasa, reduce massivning birinchi elementini dastlabki qiymat sifatida qabul qiladi va iteratsiyani ikkinchi elementdan boshlaydi.

Hisoblash jadvali yuqoridagidek, birinchi qatorni chiqarib tashlagan holda.

Ammo bunday foydalanishda katta ehtiyotkorlik talab qilinadi. Agar massiv bo'sh bo'lsa, dastlabki qiymatsiz reduce chaqiruvi xatoga olib keladi.

Mana bir misol:

let arr = [];
 
// Xato: Bo'sh massivda dastlabki qiymatsiz reduce chaqiruvi
// agar dastlabki qiymat mavjud bo'lsa, reduce bo'sh massiv uchun uni qaytarardi.
arr.reduce((sum, current) => sum + current);

Shuning uchun har doim dastlabki qiymatni ko'rsatish tavsiya etiladi.

arr.reduceRight usuli ham xuddi shunday qiladi, lekin chapdan o'ngga qarab yuradi.

Array.isArray

Massivlar alohida til turi hosil qilmaydi. Ular obyektlar asosida tuzilgan.

Shunday qilib, typeof oddiy obyektni massivdan ajratishga yordam bermaydi:

alert(typeof {}); // object
alert(typeof []); // object (bir xil)

...Ammo massivlar juda ko'p ishlatilgani uchun buni aniqlash uchun maxsus usul mavjud: Array.isArray(value). U qiymat massiv ekanligini qaytaradi, aks holda false qaytaradi.

alert(Array.isArray({})); // false
alert(Array.isArray([])); // true

Ko'p usullar "thisArg"ni qo‘llab-quvvatlaydi

Deyarli barcha massiv usullari funksiyalarni chaqiradi – masalan, find, filter, map, ammo sezilarli istisno bu sort. Ular ixtiyoriy qo‘shimcha parametr thisArgni qabul qiladi.

Bu parametr yuqoridagi bo‘limlarda tushuntirilmagan, chunki u kamdan-kam hollarda ishlatiladi. Ammo to'liq bo'lish uchun biz uni yoritishimiz kerak.

Mana bu usullarning to'liq sintaksisi:

arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);

thisArg parametri funksiyada thisga aylanadi.

Misol uchun, biz armiyaning obyektining usulini filtr sifatida ishlatamiz va thisArg kontekstni uzatadi:

let army = {
  minAge: 18,
  maxAge: 27,
  canJoin(user) {
    return user.age >= this.minAge && user.age < this.maxAge;
  },
};
 
let users = [{ age: 16 }, { age: 20 }, { age: 23 }, { age: 30 }];
 
// army.canJoin qaytargan true qiymatga ega foydalanuvchilarni toping
let soldiers = users.filter(army.canJoin, army);
 
alert(soldiers.length); // 2
alert(soldiers[0].age); // 20
alert(soldiers[1].age); // 23

Agar yuqoridagi misolda biz users.filter(army.canJoin) dan foydalangan bo'lsak, unda army.canJoin mustaqil funksiya sifatida chaqiriladi, this=undefined bilan, bu esa darhol xatoga olib keladi.

users.filter(army.canJoin, army) chaqiruvi users.filter(user => army.canJoin(user)) bilan almashtirilishi mumkin, bu xuddi shunday ishlaydi. Ikkinchisi odatda ko'proq ishlatiladi, chunki bu ko'pchilik uchun tushunarliroq.

Xulosa

Massiv usullarining qo‘llanma varaqasi:

Elementlarni qo'shish/o'chirish uchun:

  • push(...items) – elementlarni oxiriga qo'shadi,
  • pop() – elementni oxiridan chiqaradi,
  • shift() – elementni boshidan chiqaradi,
  • unshift(...items) – elementlarni boshiga qo'shadi.
  • splice(pos, deleteCount, ...items) – indeks posda deleteCount elementni o'chiradi va itemsni kiritadi.
  • slice(start, end) – yangi massiv yaratadi, start indeksidan endgacha (shu jumladan emas) elementlarni nusxalaydi.
  • concat(...items) – yangi massiv qaytaradi: joriy massivning barcha elementlarini nusxalaydi va itemsni unga qo'shadi. Agar items dan biri massiv bo'lsa, uning elementlari olinadi.

Elementlar orasida qidirish uchun:

  • indexOf/lastIndexOf(item, pos)pos dan boshlab elementni qidiring va indeksni qaytaring yoki topilmasa -1.
  • includes(value) – massivda value mavjud bo'lsa, true, aks holda false qaytaradi.
  • find/filter(func) – funktsiya orqali elementlarni filtrlaydi, true qaytaradigan birinchi yoki barcha qiymatlarni qaytaradi.
  • findIndex xuddi find kabi, lekin qiymat o'rniga indeksni qaytaradi.

Elementlarni takrorlash uchun:

  • forEach(func) – har bir element uchun funcni chaqiradi, hech narsa qaytarmaydi.

Massivni o'zgartirish uchun:

  • map(func) – har bir element uchun funcni chaqirib, natijadan yangi massiv yaratadi.
  • sort(func) – massivni o‘z joyida saralaydi va uni qaytaradi.
  • reverse() – massivni o‘z joyida teskari aylantiradi va uni qaytaradi.
  • split/join – qatorni massivga aylantiradi va aksincha.
  • reduce/reduceRight(func, initial) – massiv bo'ylab yagona qiymatni hisoblaydi, har bir element uchun funcni chaqiradi va chaqiruvlar orasida oraliq natijani uzatadi.

Qo‘shimcha:

  • Array.isArray(value) qiymat massiv ekanligini tekshiradi, agar shunday bo'lsa true, aks holda false qaytaradi.

Diqqat qiling, sort, reverse va splice usullari massivning o‘zini o‘zgartiradi.

Bu usullar eng ko‘p ishlatiladiganlari bo‘lib, ular 99% foydalanish holatlarini qamrab oladi. Ammo bir nechta boshqalar ham mavjud:

  • arr.some(fn)/arr.every(fn) massivni tekshiradi.

Funktsiya fn xuddi map kabi massivning har bir elementi uchun chaqiriladi. Agar natijalardan biri/barchasi true bo‘lsa, true qaytaradi, aks holda false.

Ushbu usullar || va && operatorlariga o'xshash ishlaydi: agar fn some() chaqirilganida haqiqiy qiymat qaytarsa, arr.some() darhol true qaytaradi va qolgan elementlarni tekshirishni to'xtatadi; agar fn every() da yolg'on qiymat qaytarsa, arr.every() darhol false qaytaradi va qolgan elementlarni tekshirishni to'xtatadi.

Biz every yordamida massivlarni solishtirishimiz mumkin:

function arraysEqual(arr1, arr2) {
  return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
}
 
alert(arraysEqual([1, 2], [1, 2])); // true
  1. arr.fill(value, start, end) – massivni start dan end gacha bo'lgan indekslar orasida takrorlanuvchi qiymat bilan to'ldiradi.

  2. arr.copyWithin(target, start, end)start dan end gacha bo'lgan elementlarni o'zida, target pozitsiyasida nusxalaydi (mavjudlarini o'zgartiradi).

  3. arr.flat(depth)/arr.flatMap(fn) – ko'p o'lchovli massivdan tekis massiv yaratadi.

To‘liq ro‘yxat uchun qo‘llanmaga qarang.

Dastlab, juda ko'p usullar bor kabi tuyulishi mumkin, ular yodda saqlash qiyin. Ammo aslida bu juda oson.

Qo‘llanmani ko‘rib chiqing, shunda ular bilan tanish bo‘lasiz. Keyin ushbu bobdagi vazifalarni yeching, shunda massiv usullari bilan tajribaga ega bo'lasiz.

Keyinchalik, agar siz massiv bilan biror narsa qilishni xohlasangiz va qanday qilishni bilmasangiz – bu yerga keling, qo‘llanmaga qarang va to‘g‘ri usulni toping. Misollar sizga to'g'ri yozishga yordam beradi. Tez orada siz usullarni avtomatik ravishda eslay olasiz, hech qanday maxsus harakatlarsiz.

Ushbu sahifada

GitHubda tahrirlash