Към съдържанието
wannacode

Масиви

Проблем

Тридесет оценки

Учител пише програма за оценките от контролното. За петима ученици прави пет променливи:

const score1 = 62;
const score2 = 85;
const score3 = 47;
const score4 = 91;
const score5 = 78;
const sum = score1 + score2 + score3 + score4 + score5;
console.log(sum);
Изход
363

В класа обаче са тридесет. Тридесет променливи, тридесет събираеми. А за най-високия резултат трябват още двадесет и девет сравнения. Цикълът от миналия урок не помага: той не може да мине през score1, score2, score3, защото това са отделни имена. Трябват ни много стойности под едно име.

Идеята

Масив: редица от кутийки

Масивът (array) е редица от стойности под едно име. Всяка стойност, елемент на масива, има номер: индекс. Индексите започват от 0.

const scores = [62, 85, 47, 91, 78];
console.log(scores[0]);
console.log(scores[4]);
console.log(scores.length);
scores[2] = 50;
console.log(scores[2]);
Изход
62
78
5
50
  • Стойностите са в квадратни скоби [ ], разделени със запетая. Тип не се пише. Може да смесиш числа и текст в един масив, но обикновено всички елементи са от един вид.
  • scores[0] е първият елемент, scores[4] петият. Последният индекс е винаги с 1 по-малък от броя.
  • scores.length е броят на елементите. Тук е 5.
  • scores[2] = 50; сменя един елемент. Останалите не се променят.

Ако още не знаеш стойностите, но знаеш колко са, създаваш масив с размер и го пълниш с нули:

const booked = new Array(3).fill(0);
booked[1] = 2;
console.log(booked);
Изход
[ 0, 2, 0 ]

За разлика от масивите в Java и C#, масивът расте. push добавя елемент в края:

const scores = [62, 85];
scores.push(47);
console.log(scores);
console.log(scores.length);
Изход
[ 62, 85, 47 ]
3

console.log на цял масив показва елементите му в [ ]. Удобно е за проверка, а за изхода на програмата обикновено печаташ елементите сам.

По-точно, за любопитните
  • new Array(3) без .fill(0) не съдържа нули, а три празни места. Прочетеш ли едно от тях, получаваш undefined, „няма стойност“:
const booked = new Array(3);
console.log(booked);
console.log(booked[0]);
Изход
[ <3 empty items> ]
undefined
  • const пред масива значи, че името винаги сочи същия масив. Елементите му пак може да се сменят и да се добавят.

Пробвай

Кой е кой?

Какво ще отпечата програмата?

const prices = [12, 7, 30, 5];
console.log(prices[1] + prices[3]);
Отговор

Идеята

Цикъл през масива

Сега цикълът вече помага. Броячът i става индексът: минава от 0 до последния и на всеки кръг взима scores[i].

const scores = [62, 85, 47, 91, 78];
let sum = 0;
for (let i = 0; i < scores.length; i++) {
sum += scores[i];
}
console.log(`Сума: ${sum}`);
console.log(`Средно: ${sum / scores.length}`);
Изход
Сума: 363
Средно: 72.6

Условието е i < scores.length, а не число. Добавиш ли тридесетте оценки в [ ], цикълът не се променя. Това се казва обхождане на масива.

В JavaScript / дели точно, затова средното излиза 72,6 без нищо допълнително.

Виж го

Най-високият резултат

Как намираш най-голямото число? Помниш най-голямото засега и го сменяш, щом видиш по-голямо.

const scores = [62, 85, 47, 91, 78];
let max = scores[0];
for (let i = 1; i < scores.length; i++) {
if (scores[i] > max) {
max = scores[i];
}
}
console.log(max); // 91
Стъпка по стъпка, с думи
  1. Пет резултата, едно до друго. Номерата отдолу са индексите: от 0 до 4.
  2. max започва от първия: scores[0] е 62.
  3. i = 1: 85 > 62, max става 85.
  4. i = 2: 47 не е по-голямо от 85. Нищо не се сменя.
  5. i = 3: 91 > 85, max става 91.
  6. i = 4: 78 < 91. После i става 5, 5 < scores.length е невярно и цикълът спира.
  7. Най-големият резултат е 91.

max започва от първия елемент, а не от 0. Ако всички резултати бяха отрицателни, 0 щеше да „спечели“, без да е в масива. Затова и цикълът започва от i = 1: първият вече е в max.

Пробвай

Намери грешката

Програмата трябва да отпечата сумата 363. Вместо това отпечатва NaN.

На кой ред е грешката?

Избери реда с грешката.

Идеята

for … of: за всеки елемент

Когато ти трябват само стойностите, а не индексите, има по-кратък цикъл. Колко ученици са взели изпита (60 точки и нагоре)?

const scores = [62, 85, 47, 91, 78];
let passed = 0;
for (const score of scores) {
if (score >= 60) {
passed++;
}
}
console.log(`Взели изпита: ${passed}`);
Изход
Взели изпита: 4

Чете се „за всяко score от (of) scores“. В Java същото се казва for-each. Няма брояч и индекс, затова не можеш да излезеш извън масива. const е, защото score в един кръг не се сменя: следващият кръг получава ново score.

Има и граница: не знаеш на кой индекс си и не можеш да смениш елемента.

По-точно, за любопитните
  • Ако напишеш score = 0; в тялото, програмата спира с TypeError, защото score е const. С let score щеше да смени само променливата, а масивът щеше да остане същият.
const scores = [62, 85, 47];
try {
for (const score of scores) {
score = 0;
}
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
Изход
TypeError: Assignment to constant variable.
  • Има и for … in, с in вместо of. Той не дава елементите, а индексите, и то като текст. За масиви пиши for … of:
const scores = [62, 85, 47];
for (const i in scores) {
console.log(i, typeof i);
}
Изход
0 string
1 string
2 string
  • За да смениш елементи или да ти трябва индексът (например за съседа scores[i + 1]), ползвай обикновен for.

Пробвай

Две имена, един масив

Какво ще отпечата програмата?

const prices = [10, 20, 30];
const sale = prices;
sale[0] = 5;
console.log(prices[0]);
Отговор
По-точно, за любопитните

Истинско копие правиш с нов масив и цикъл:

const prices = [10, 20, 30];
const sale = [];
for (const price of prices) {
sale.push(price);
}
sale[0] = 5;
console.log(prices[0], sale[0]);
Изход
10 5

Същото дава и [...prices]:

const prices = [10, 20, 30];
const sale = [...prices];
sale[0] = 5;
console.log(prices[0], sale[0]);
Изход
10 5

Пробвай

Подреди редовете

Програмата трябва да намери най-ниската цена. Подреди редовете ѝ.

  1. min = prices[i];
  2. let min = prices[0];
  3. if (prices[i] < min) {
  4. const prices = [18, 7, 25, 12];
  5. for (let i = 1; i < prices.length; i++) {
  6. }
  7. }
  8. console.log(min);

Упражнение

Сметки с масив

Реши задачите в редактора си. Всяка програма първо чете колко са числата, после самите числа, по едно на ред:

const n = Number(await ask(""));
const numbers = [];
for (let i = 0; i < n; i++) {
numbers.push(Number(await ask("")));
}

Най-горе са редовете за ask(), а накрая rl.close(), както в урока „Вход и изход“.

1. Сума, четни, нечетни. Отпечатай сумата на всички числа, на четните и на нечетните.

Вход
3
1
2
3
Изход
Сума: 6
Четни: 2
Нечетни: 4

2. Обратно. Създай нов масив със същите числа в обратен ред и го отпечатай на един ред.

Вход
5
1
2
3
4
5
Изход
5 4 3 2 1

3. Най-малко, най-голямо, средно. С едно минаване през масива.

Вход
5
4
9
2
7
4
Изход
Най-малко: 2
Най-голямо: 9
Средно: 5.2

Упражнение

Подреждане и броене

4. Сортиране. Подреди числата от най-малкото към най-голямото. Подсказка: минавай през масива и ако два съседа са в грешен ред, разменяй ги (размяната е от урока за променливите). Повтори, докато няма какво да размениш. Това се казва метод на мехурчето: големите числа „изплуват“ към края.

Вход
5
42
7
19
3
25
Изход
3 7 19 25 42

5. Най-дълга поредица. Намери най-дългата поредица от еднакви числа едно след друго.

Вход
8
2
2
5
5
5
1
5
5
Изход
Числото 5, 3 пъти подред

6. Статистика на оценките. Прочети оценки от 2 до 6 и отпечатай колко са от всеки вид. Подсказка: масив counts с 7 елемента, в който оценката е индексът: counts[grade]++.

Вход
8
6
4
5
6
3
2
6
5
Изход
Отличен (6): 3
Много добър (5): 2
Добър (4): 1
Среден (3): 1
Слаб (2): 1

Още задачи

  • Медиана. Числото по средата на сортирания масив. За 3, 1, 4, 1, 5 медианата е 3. Ако числата са четен брой, медианата е средното на двете в средата: за 7, 1, 3, 9 е (3 + 7) / 2 = 5.
  • Симетричен масив. Чете ли се еднакво отпред назад и отзад напред? 1 2 3 2 1 е симетричен, 1 2 3 1 не е.
  • Римски цифри. Прочети число от 1 до 3999 и го отпечатай с римски цифри: 2024 е MMXXIV, 1994 е MCMXCIV. Подсказка: два масива един до друг, със стойностите 1000, 900, 500, 400, 100, …, 1 и със знаците "M", "CM", "D", "CD", "C", …, "I". Докато числото е поне колкото текущата стойност, добавяй знака и вади стойността.

Тест

Провери се

1. В масива [62, 85, 47, 91, 78] на кой индекс е 91?
2. Кой е индексът на последния елемент на масива scores?
3. Какво има в масива веднага след const booked = new Array(3).fill(0);?
4. Масивът scores има 5 елемента. Какво дава scores[5]?
5. const sale = prices; sale[0] = 5; Какво става с prices[0]?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·