Български
Тема
Български
Тема
Днес ще се научите да:
length;push, pop, unshift, shift и splice; да копирате част със slice; да четете от края с at;indexOf, includes и lastIndexOf и да замените верига от === с allow-list;split и да слепвате array в string с join - за версии и за стойности, пакетирани в един string ("a;b", "key=value,key=value");for и for...of, напред и назад;Array.isArray;Къде е мястото на тази тема в примерното правило за проверка - днешното правило проверява списък: enclosures на устройството. Числото във всеки коментар е разделът от тази лекция:
// 6. an allow-list instead of a chain of ===
const MODELS = ["XR-210", "XR-220"];
function rule(doc) {
if (!doc.overview || !doc.inventory?.details) return;
const model = doc.overview.model;
if (!MODELS.includes(model)) return; // 6. includes
// 7. split: the version is the first word of the firmware level
const level = doc.inventory.details.firmware_level ?? "";
const version = level.split(" ")[0]; // 2, 7. split
// 10. a list of records: check every enclosure, collect the problems
const enclosures = doc.inventory.enclosures;
if (!Array.isArray(enclosures)) return; // 4. isArray
const problems = []; // 1. empty array
for (const enc of enclosures) { // 8. for...of
if (enc.state !== "normal") {
problems.push(`${enc.id} is ${enc.state}`); // 5. push
}
if (versionBelow(enc.firmware, "2.14")) {
problems.push(`${enc.id} has firmware ${enc.firmware}`);
}
}
if (problems.length === 0) return; // 3. length
const name = doc.inventory.extended_info?.json?.data?.system?.name
|| doc.status.device_alias;
emit(name, {
"Model": model,
"Version": version,
"Problems": problems.length,
"Details": problems.join("; ") // 7. join
});
}versionBelow(a, b) все още е готовата helper функция от лекция 3; собствена ще напишем в занятие 5.doc.inventory.features (strings) и doc.inventory.enclosures (обекти). В раздел 11 ще изпълните това правило и ще прочетете същото правило в по-стария стил.Истинските отчети са пълни със списъци: дискове, възли, enclosures, pools и дълги списъци от записи ключ / стойност. Правилата ги обхождат, търсят в тях и строят нови списъци за текста на alert-а.
Досегашните типове - number, string, boolean, null, undefined - са примитивни (прости) типове: по една стойност. Array е съставен тип: подреден списък от стойности, събрани под едно име.
["a", "b"] и ["b", "a"] са различни arrays.// an array of strings
let colors = ["red", "orange", "yellow", "green", "blue"];
// an array with values of different types
let user = ["Ada", 34, true];
// an empty array
let arr = [];
console.log(colors);
console.log(user);
console.log(arr);// an array of strings
let colors = ["red", "orange", "yellow", "green", "blue"];
// an array with values of different types
let user = ["Ada", 34, true];
// an empty array
let arr = [];
console.log(colors);
console.log(user);
console.log(arr);Елементите се пишат в квадратни скоби [ ], разделени със запетаи. Запетая след последния елемент (trailing comma) е позволена - тя улеснява редактирането на дълги списъци с по един елемент на ред:
const MODELS = [
"XR-210",
"XR-220",
"XR-230", // trailing comma: fine
];
console.log(MODELS.length); // 3
const features = doc.inventory.features; // an array from the report
console.log(features); // [ 'dedup', 'compression', 'encryption' ]const MODELS = [
"XR-210",
"XR-220",
"XR-230", // trailing comma: fine
];
console.log(MODELS.length); // 3
const features = doc.inventory.features; // an array from the report
console.log(features); // [ 'dedup', 'compression', 'encryption' ]В паметта variable-ата не пази самите елементи. Тя пази референция - стрелка - към array-я, който живее в отделна област от паметта (heap). Това е важно в раздел 4.
Анимация
Arrays в паметта: 1. Индекси
[] и по-старото new Array() Винаги създавайте arrays с [...]. В по-стар код ще видите и конструктора Array. Той има капан: с едно число не създава array с това число, а празен array с тази дължина:
const a = new Array("XR-210", "XR-220"); // same as ["XR-210", "XR-220"]
const b = new Array(3); // NOT [3]: 3 empty places
const c = Array.of(3); // [3] (ES2015)
console.log(a, a.length);
console.log(b.length); // 3
console.log(c, c.length); // [ 3 ] 1const a = new Array("XR-210", "XR-220"); // same as ["XR-210", "XR-220"]
const b = new Array(3); // NOT [3]: 3 empty places
const c = Array.of(3); // [3] (ES2015)
console.log(a, a.length);
console.log(b.length); // 3
console.log(c, c.length); // [ 3 ] 1Array.from (ES2015) създава array от нещо, което прилича на списък - например string:
console.log(Array.from("4.2")); // [ '4', '.', '2' ]
console.log([..."4.2"]); // the same with spread (section 5)console.log(Array.from("4.2")); // [ '4', '.', '2' ]
console.log([..."4.2"]); // the same with spread (section 5)Елементът се чете или променя с името на array-я, после индекса в квадратни скоби: arrayName[i]. arrayName[i] е израз - винаги дава стойност: елемента.
// define the array fruits
let fruits = ["apple", "banana", "strawberry"];
// read the first element
console.log("first fruit is:", fruits[0]);
// write the third element
fruits[2] = "orange";
// print the whole array
console.log("fruits =", fruits); // [ 'apple', 'banana', 'orange' ]// define the array fruits
let fruits = ["apple", "banana", "strawberry"];
// read the first element
console.log("first fruit is:", fruits[0]);
// write the third element
fruits[2] = "orange";
// print the whole array
console.log("fruits =", fruits); // [ 'apple', 'banana', 'orange' ]С отчета:
const features = doc.inventory.features;
console.log(features[0]); // dedup
console.log(features[1]); // compression
const enclosures = doc.inventory.enclosures;
console.log(enclosures[0].id); // ENC-01 - first the index, then the keyconst features = doc.inventory.features;
console.log(features[0]); // dedup
console.log(features[1]); // compression
const enclosures = doc.inventory.enclosures;
console.log(enclosures[0].id); // ENC-01 - first the index, then the keyИндексът е цяло число от 0 нагоре. Работи и variable или израз:
const parts = ["4", "2", "1", "7"];
const i = 2;
console.log(parts[i]); // 1
console.log(parts[i + 1]); // 7const parts = ["4", "2", "1", "7"];
const i = 2;
console.log(parts[i]); // 1
console.log(parts[i + 1]); // 7Няма грешка. Получавате undefined:
let arr = [2, 4, 6];
console.log(arr[4]); // undefined
console.log(arr[-1]); // undefined - -1 does not count from the end herelet arr = [2, 4, 6];
console.log(arr[4]); // undefined
console.log(arr[-1]); // undefined - -1 does not count from the end hereТихо undefined
Грешният индекс не е грешка, затова бъгът се появява по-късно - например като "undefined" в текста на alert. Когато четете елемент по индекс, питайте се: може ли array-ят да е по-къс, отколкото мисля?
Елементът се записва там, а местата между тях стават празни (holes):
let arr = [2, 4, 6];
arr[4] = 8;
console.log(arr); // [ 2, 4, 6, <1 empty item>, 8 ]
console.log(arr.length); // 5
console.log(arr[3]); // undefinedlet arr = [2, 4, 6];
arr[4] = 8;
console.log(arr); // [ 2, 4, 6, <1 empty item>, 8 ]
console.log(arr.length); // 5
console.log(arr[3]); // undefined(<1 empty item> е начинът, по който Node.js и тази страница показват дупка.)
MDN: Array.prototype.at()
Индексът на последния елемент е length - 1:
let arr = [2, 4, 6];
console.log(arr[arr.length - 1]); // 6
console.log(arr.at(-1)); // 6 - at() counts from the end: -1, -2 ...
console.log(arr.at(0)); // 2 - at(0) is the same as arr[0]let arr = [2, 4, 6];
console.log(arr[arr.length - 1]); // 6
console.log(arr.at(-1)); // 6 - at() counts from the end: -1, -2 ...
console.log(arr.at(0)); // 2 - at(0) is the same as arr[0]ES2022+
at() е ES2022 - проверете дали rule engine-ът ви го поддържа. Формата arr[arr.length - 1] работи навсякъде.
Анимация
Arrays в паметта: 1. Индекси, 2. Извън границите
length MDN: Array: length
arrayName.length е броят на елементите. Той е динамичен: променя се, когато array-ят се промени - и записът в него променя array-я:
let arr = [2, 4, 6];
console.log(arr.length); // 3
// make the array longer: the new places are empty
arr.length = 5;
console.log(arr); // [ 2, 4, 6, <2 empty items> ]
// make it shorter: the elements at the end are deleted
arr.length = 2;
console.log(arr); // [ 2, 4 ]
// delete all elements
arr.length = 0;
console.log(arr); // []let arr = [2, 4, 6];
console.log(arr.length); // 3
// make the array longer: the new places are empty
arr.length = 5;
console.log(arr); // [ 2, 4, 6, <2 empty items> ]
// make it shorter: the elements at the end are deleted
arr.length = 2;
console.log(arr); // [ 2, 4 ]
// delete all elements
arr.length = 0;
console.log(arr); // []length винаги е с едно повече от последния индекс. Празният array има length 0.
const enclosures = doc.inventory.enclosures;
console.log(enclosures.length); // 4
console.log(enclosures[enclosures.length - 1].id); // ENC-04const enclosures = doc.inventory.enclosures;
console.log(enclosures.length); // 4
console.log(enclosures[enclosures.length - 1].id); // ENC-04= вместо === при проверка за празнота:
let arr = [2, 4, 6];
if (arr.length = 0) { // assignment, not comparison!
console.log("Empty array!");
}
console.log(arr); // [] - we deleted all elements by mistakelet arr = [2, 4, 6];
if (arr.length = 0) { // assignment, not comparison!
console.log("Empty array!");
}
console.log(arr); // [] - we deleted all elements by mistakearr.length = 0 е присвояване. То изтрива елементите и дава 0, което е falsy, така че if-ът дори не се изпълнява. Правилният код:
let arr = [2, 4, 6];
if (arr.length === 0) {
console.log("Empty array!");
}
console.log(arr); // [ 2, 4, 6 ]let arr = [2, 4, 6];
if (arr.length === 0) {
console.log("Empty array!");
}
console.log(arr); // [ 2, 4, 6 ]Правописна грешка в length:
let arr = [2, 4, 6];
if (arr.lenght) { // lenght, not length
console.log("Non-empty array");
} else {
console.log("Empty array"); // this runs!
}let arr = [2, 4, 6];
if (arr.lenght) { // lenght, not length
console.log("Non-empty array");
} else {
console.log("Empty array"); // this runs!
}arr.lenght не е грешка: array-ят няма property lenght, затова стойността е undefined - falsy. Правилната проверка if (arr.length) отпечатва Non-empty array. (Защо няма грешка - раздел 4 и занятие 6.) Редакторът ви помага тук: VSCode подчертава непознатите properties, когато файлът започва с // @ts-check (занятие 10).
| Проверка | Означава |
|---|---|
arr.length === 0 | array-ят е празен |
arr.length > 0 | има поне един елемент |
if (arr.length) | същото, кратка форма (0 е falsy) |
if (arr) | не е проверка за празнота - [] е truthy! |
const problems = [];
console.log(Boolean(problems)); // true - an empty array is truthy
console.log(problems.length === 0); // true - the right checkconst problems = [];
console.log(Boolean(problems)); // true - an empty array is truthy
console.log(problems.length === 0); // true - the right checkАнимация
Arrays в паметта: 2. Извън границите (запис в length и бъгът с =)
В JavaScript arrays са реализирани като обекти:
let arr = [2, 4, 6];
console.log(typeof arr); // object
console.log(Array.isArray(arr)); // true - the right check
console.log(Array.isArray("2,4,6")); // false
console.log(Array.isArray({})); // falselet arr = [2, 4, 6];
console.log(typeof arr); // object
console.log(Array.isArray(arr)); // true - the right check
console.log(Array.isArray("2,4,6")); // false
console.log(Array.isArray({})); // falseИндексите са ключовете "0", "1", "2" на този обект. Затова грешен "индекс" не дава грешка - той просто става обикновено property на обекта и не се брои в length:
let arr = [2, 4, 6];
arr[-1] = 0; // not a valid index - becomes a property named "-1"
arr["a"] = 10; // not a valid index - becomes a property named "a"
console.log(arr.length); // 3
console.log(arr["-1"], arr.a); // 0 10
console.log(arr[1], arr["1"]); // 4 4 - the index 1 is the key "1"
// Node.js prints arr as [ 2, 4, 6, '-1': 0, a: 10 ]let arr = [2, 4, 6];
arr[-1] = 0; // not a valid index - becomes a property named "-1"
arr["a"] = 10; // not a valid index - becomes a property named "a"
console.log(arr.length); // 3
console.log(arr["-1"], arr.a); // 0 10
console.log(arr[1], arr["1"]); // 4 4 - the index 1 is the key "1"
// Node.js prints arr as [ 2, 4, 6, '-1': 0, a: 10 ]Arrays идват и с вградени properties и методи (функции, закачени за array-я) за честите операции: length, push, pop, slice и много други. Не е нужно да ги учите наизуст - те са добре документирани в MDN: Array. Днес ще видите тези, които правилата използват най-често; останалите (map, filter, sort, ...) идват в занятие 7.
Array.isArray MDN: Array.isArray()
JSON данните не винаги имат формата, която очаквате. Списъкът може да липсва, да е null или да е един обект вместо списък с един обект. typeof не може да различи array от обект - Array.isArray може:
console.log(typeof [], typeof {}, typeof null); // object object object
console.log(Array.isArray(doc.inventory.enclosures)); // true
console.log(Array.isArray(doc.inventory.details)); // false - an object
console.log(Array.isArray(doc.inventory.missing)); // false - undefinedconsole.log(typeof [], typeof {}, typeof null); // object object object
console.log(Array.isArray(doc.inventory.enclosures)); // true
console.log(Array.isArray(doc.inventory.details)); // false - an object
console.log(Array.isArray(doc.inventory.missing)); // false - undefinedVariable-ата пази референция към array-я, а не самия array. Копирането на variable-ата копира референцията - двете имена сочат към същия array:
const fruits = ["apple", "banana"];
let b = fruits; // no new array - one more arrow to the same one
b.push("kiwi");
console.log(fruits); // [ 'apple', 'banana', 'kiwi' ] - fruits changed too!
console.log(b === fruits); // true - the same array
console.log([1, 2] === [1, 2]); // false - two different arraysconst fruits = ["apple", "banana"];
let b = fruits; // no new array - one more arrow to the same one
b.push("kiwi");
console.log(fruits); // [ 'apple', 'banana', 'kiwi' ] - fruits changed too!
console.log(b === fruits); // true - the same array
console.log([1, 2] === [1, 2]); // false - two different arrays=== сравнява arrays по референция: "същият array ли е?", а не "еднакви ли са елементите?".const пази variable-ата (стрелката), а не array-я: fruits.push(...) и fruits[0] = ... са позволени, fruits = [...] е грешка.const fruits = ["apple", "banana"];
fruits[0] = "mango"; // allowed - the array changes
console.log(fruits);
// fruits = ["plum"]; // TypeError: Assignment to constant variable.const fruits = ["apple", "banana"];
fruits[0] = "mango"; // allowed - the array changes
console.log(fruits);
// fruits = ["plum"]; // TypeError: Assignment to constant variable.Копие е нов array със същите елементи: arr.slice() или [...arr] (раздел 5).
const original = doc.inventory.features;
const copy = [...original]; // a new array
copy.push("snapshots");
console.log(original.length, copy.length); // 3 4 - the report is not changedconst original = doc.inventory.features;
const copy = [...original]; // a new array
copy.push("snapshots");
console.log(original.length, copy.length); // 3 4 - the report is not changedНе променяйте отчета
В правилото doc принадлежи на engine-а. Ако една част от правилото промени списък от doc (с push, splice, sort, length = 0 ...), всеки следващ ред вижда променените данни. Работете с копие или създайте нов array.
Сравняване на два arrays
За да проверите дали два кратки arrays от strings или числа имат еднакви елементи, сравнете текста им: a.join(",") === b.join(",") (раздел 7). Проверките в домашните на този курс правят същото с JSON.stringify (занятие 9).
Анимации
Arrays в паметта: 3. Array-ят е обект, 5. Референции
push, pop, unshift, shift MDN: Array.prototype.push(), Array.prototype.pop(), Array.prototype.unshift(), Array.prototype.shift()
| Метод | Прави | Връща |
|---|---|---|
arr.push(x, ...) | добавя в края | новата дължина |
arr.pop() | премахва последния елемент | премахнатия елемент |
arr.unshift(x, ...) | добавя в началото | новата дължина |
arr.shift() | премахва първия елемент | премахнатия елемент |
Как да четете страница от референцията на MDN
Отворете страницата на push по-горе. Всяка страница от референцията има едни и същи части:
Array.prototype.push() е метод на всеки array: извиквате го като arr.push(...). Заглавие без prototype, като Array.isArray(), се извиква на самия Array: Array.isArray(x).push: новата дължина, а не array-я - колоната "Връща" в таблицата по-горе.let fruits = ["apple", "banana", "strawberry"];
// number of elements
console.log(fruits.length); // 3
// add an element at the start
fruits.unshift("orange");
console.log(fruits); // [ 'orange', 'apple', 'banana', 'strawberry' ]
// add an element at the end
fruits.push("lemon");
console.log(fruits); // [ 'orange', 'apple', 'banana', 'strawberry', 'lemon' ]
// remove the first element
fruits.shift();
console.log(fruits); // [ 'apple', 'banana', 'strawberry', 'lemon' ]
// remove the last element
fruits.pop();
console.log(fruits); // [ 'apple', 'banana', 'strawberry' ]let fruits = ["apple", "banana", "strawberry"];
// number of elements
console.log(fruits.length); // 3
// add an element at the start
fruits.unshift("orange");
console.log(fruits); // [ 'orange', 'apple', 'banana', 'strawberry' ]
// add an element at the end
fruits.push("lemon");
console.log(fruits); // [ 'orange', 'apple', 'banana', 'strawberry', 'lemon' ]
// remove the first element
fruits.shift();
console.log(fruits); // [ 'apple', 'banana', 'strawberry', 'lemon' ]
// remove the last element
fruits.pop();
console.log(fruits); // [ 'apple', 'banana', 'strawberry' ]Върнатите стойности също са полезни - пример с дъгата:
const rainbow = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
// in a template string, the elements are joined with ","
console.log(`rainbow: ${rainbow}`);
const lastElement = rainbow.pop(); // pop returns the removed element
console.log(`last element: ${lastElement}`);
const firstElement = rainbow.shift(); // so does shift
console.log(`first element: ${firstElement}`);
rainbow.push("black", "white"); // push accepts several elements
console.log(`after push: ${rainbow}`);
rainbow.unshift("black", "white"); // so does unshift
console.log(`after unshift: ${rainbow}`);
rainbow.reverse(); // reverses the array ITSELF
console.log(`after reverse: ${rainbow}`);const rainbow = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
// in a template string, the elements are joined with ","
console.log(`rainbow: ${rainbow}`);
const lastElement = rainbow.pop(); // pop returns the removed element
console.log(`last element: ${lastElement}`);
const firstElement = rainbow.shift(); // so does shift
console.log(`first element: ${firstElement}`);
rainbow.push("black", "white"); // push accepts several elements
console.log(`after push: ${rainbow}`);
rainbow.unshift("black", "white"); // so does unshift
console.log(`after unshift: ${rainbow}`);
rainbow.reverse(); // reverses the array ITSELF
console.log(`after reverse: ${rainbow}`);push е методът, който ще пишете най-често: той строи нов списък, по един елемент. Така правилото събира редовете на един alert:
const problems = [];
for (const enc of doc.inventory.enclosures) {
if (enc.state !== "normal") {
problems.push(enc.id + " is " + enc.state);
}
}
console.log(problems); // [ 'ENC-03 is degraded' ]
console.log(problems.length); // 1const problems = [];
for (const enc of doc.inventory.enclosures) {
if (enc.state !== "normal") {
problems.push(enc.id + " is " + enc.state);
}
}
console.log(problems); // [ 'ENC-03 is degraded' ]
console.log(problems.length); // 1Анимация
Arrays в паметта: 4. push / pop / unshift / shift
slice - копиране на част arr.slice(start, end) връща нов array с елементите от start до end, без да го включва. Оригиналният array не се променя.
const parts = ["4", "2", "1", "7"];
console.log(parts.slice(0, 2)); // [ '4', '2' ] - indexes 0 and 1
console.log(parts.slice(1, 3)); // [ '2', '1' ]
console.log(parts.slice(2)); // [ '1', '7' ] - without end: to the end
console.log(parts.slice(-2)); // [ '1', '7' ] - negative: from the end
console.log(parts.slice()); // [ '4', '2', '1', '7' ] - a copy of all
console.log(parts); // unchangedconst parts = ["4", "2", "1", "7"];
console.log(parts.slice(0, 2)); // [ '4', '2' ] - indexes 0 and 1
console.log(parts.slice(1, 3)); // [ '2', '1' ]
console.log(parts.slice(2)); // [ '1', '7' ] - without end: to the end
console.log(parts.slice(-2)); // [ '1', '7' ] - negative: from the end
console.log(parts.slice()); // [ '4', '2', '1', '7' ] - a copy of all
console.log(parts); // unchangedend - start е броят на елементите, които получавате. Първите три части на версия:
const version = doc.overview.version; // "4.2.1.7"
const major = version.split(".").slice(0, 3); // split: section 7
console.log(major); // [ '4', '2', '1' ]const version = doc.overview.version; // "4.2.1.7"
const major = version.split(".").slice(0, 3); // split: section 7
console.log(major); // [ '4', '2', '1' ]splice - премахване, вмъкване или замяна, на място arr.splice(start, deleteCount, item1, item2, ...) променя самия array: от индекс start премахва deleteCount елемента и слага новите елементи на тяхно място. Връща премахнатите елементи в нов array.
const models = ["XR-100", "XR-210", "XR-220", "XR-300"];
const removed = models.splice(1, 2); // remove 2 elements from index 1
console.log(removed); // [ 'XR-210', 'XR-220' ]
console.log(models); // [ 'XR-100', 'XR-300' ]
models.splice(1, 0, "XR-250"); // remove nothing, insert at index 1
console.log(models); // [ 'XR-100', 'XR-250', 'XR-300' ]
models.splice(0, 1, "XR-110", "XR-120"); // replace 1 element with 2
console.log(models); // [ 'XR-110', 'XR-120', 'XR-250', 'XR-300' ]const models = ["XR-100", "XR-210", "XR-220", "XR-300"];
const removed = models.splice(1, 2); // remove 2 elements from index 1
console.log(removed); // [ 'XR-210', 'XR-220' ]
console.log(models); // [ 'XR-100', 'XR-300' ]
models.splice(1, 0, "XR-250"); // remove nothing, insert at index 1
console.log(models); // [ 'XR-100', 'XR-250', 'XR-300' ]
models.splice(0, 1, "XR-110", "XR-120"); // replace 1 element with 2
console.log(models); // [ 'XR-110', 'XR-120', 'XR-250', 'XR-300' ]Премахване на един елемент, когато знаете стойността му - първо намерете индекса му (раздел 6):
const MODELS = ["XR-210", "XR-220", "XR-230"];
const pos = MODELS.indexOf("XR-220");
if (pos !== -1) { // only if it is there!
MODELS.splice(pos, 1);
}
console.log(MODELS); // [ 'XR-210', 'XR-230' ]const MODELS = ["XR-210", "XR-220", "XR-230"];
const pos = MODELS.indexOf("XR-220");
if (pos !== -1) { // only if it is there!
MODELS.splice(pos, 1);
}
console.log(MODELS); // [ 'XR-210', 'XR-230' ]splice(-1, 1), когато елементът не е намерен
Без if-а indexOf дава -1, а splice(-1, 1) премахва последния елемент - отрицателното начало се брои от края.
slice копира, splice изрязва
Имената са почти еднакви, ефектът - не. Как да запомните: splice има една буква повече и прави нещо повече - променя array-я.
Анимации
Методи на arrays: 1. slice и at, 2. splice, 7. splice в loop (advanced)
concat и spread MDN: Array.prototype.concat(), Spread syntax (...)
const ssd = ["ENC-01", "ENC-02"];
const hdd = ["ENC-03", "ENC-04"];
console.log(ssd.concat(hdd)); // [ 'ENC-01', 'ENC-02', 'ENC-03', 'ENC-04' ]
console.log([...ssd, ...hdd]); // the same with spread (ES2015)
console.log([...ssd, "ENC-09"]); // spread + one more element
console.log(ssd, hdd); // unchanged - both give a new arrayconst ssd = ["ENC-01", "ENC-02"];
const hdd = ["ENC-03", "ENC-04"];
console.log(ssd.concat(hdd)); // [ 'ENC-01', 'ENC-02', 'ENC-03', 'ENC-04' ]
console.log([...ssd, ...hdd]); // the same with spread (ES2015)
console.log([...ssd, "ENC-09"]); // spread + one more element
console.log(ssd, hdd); // unchanged - both give a new array...arr (spread) "изсипва" елементите на array-я в новите [ ]. Повече за spread в занятие 6.
| Променят array-я (mutating) | Връщат нов array или стойност |
|---|---|
push, pop, unshift, shift | slice, concat, [...a] |
splice | indexOf, includes, join, at |
reverse, sort (занятие 7) | toReversed, toSorted, toSpliced (ES2023) |
arr[i] = x, arr.length = n |
const levels = ["4.1", "4.2", "4.3"];
const reversed = levels.toReversed(); // ES2023: a reversed COPY
console.log(reversed); // [ '4.3', '4.2', '4.1' ]
console.log(levels); // [ '4.1', '4.2', '4.3' ] - unchanged
levels.reverse(); // reverses levels itself
console.log(levels); // [ '4.3', '4.2', '4.1' ]const levels = ["4.1", "4.2", "4.3"];
const reversed = levels.toReversed(); // ES2023: a reversed COPY
console.log(reversed); // [ '4.3', '4.2', '4.1' ]
console.log(levels); // [ '4.1', '4.2', '4.3' ] - unchanged
levels.reverse(); // reverses levels itself
console.log(levels); // [ '4.3', '4.2', '4.1' ]ES2023+
toReversed(), toSorted(), toSpliced() и with() са ES2023 - проверете дали rule engine-ът ви ги поддържа. arr.slice().reverse() дава обърнато копие навсякъде.
delete arr[i] в стар код delete arr[i] не премахва елемента - оставя празно място, а length не се променя. Използвайте splice(i, 1):
const a = ["x", "y", "z"];
delete a[1];
console.log(a.length, a[1]); // 3 undefined - a hole
const b = ["x", "y", "z"];
b.splice(1, 1);
console.log(b.length, b); // 2 [ 'x', 'z' ]const a = ["x", "y", "z"];
delete a[1];
console.log(a.length, a[1]); // 3 undefined - a hole
const b = ["x", "y", "z"];
b.splice(1, 1);
console.log(b.length, b); // 2 [ 'x', 'z' ]indexOf, includes, lastIndexOf MDN: Array.prototype.indexOf(), Array.prototype.includes(), Array.prototype.lastIndexOf()
| Метод | Връща |
|---|---|
arr.indexOf(x) | индексът на първото x или -1, ако няма такова |
arr.lastIndexOf(x) | индексът на последното x или -1 |
arr.includes(x) | true или false (ES2016) |
И трите сравняват с ===. Вторият аргумент казва откъде да започне: arr.indexOf(x, 3) търси от индекс 3.
const states = ["normal", "normal", "degraded", "normal"];
console.log(states.indexOf("normal")); // 0
console.log(states.lastIndexOf("normal")); // 3
console.log(states.indexOf("degraded")); // 2
console.log(states.indexOf("failed")); // -1 - not found
console.log(states.includes("failed")); // false
console.log(states.indexOf("normal", 1)); // 1 - search from index 1const states = ["normal", "normal", "degraded", "normal"];
console.log(states.indexOf("normal")); // 0
console.log(states.lastIndexOf("normal")); // 3
console.log(states.indexOf("degraded")); // 2
console.log(states.indexOf("failed")); // -1 - not found
console.log(states.includes("failed")); // false
console.log(states.indexOf("normal", 1)); // 1 - search from index 1В лекция 3 правилото проверяваше модела с &&: if (model !== "XR-210" && model !== "XR-220") return;. С пет модела този ред става труден за четене. Allow-list пази моделите в array:
const MODELS = ["XR-210", "XR-220", "XR-230"];
const model = doc.overview.model;
if (!MODELS.includes(model)) {
console.log("not our model - stop");
} else {
console.log(model + " is in the list");
}const MODELS = ["XR-210", "XR-220", "XR-230"];
const model = doc.overview.model;
if (!MODELS.includes(model)) {
console.log("not our model - stop");
} else {
console.log(model + " is in the list");
}Добавянето на модел вече е промяна в данните, а не в логиката. Списък от модели, които трябва да се пропуснат, е deny-list - същият код без !.
Същото работи за всяко фиксирано множество от стойности:
const BAD_STATES = ["degraded", "failed", "offline"];
const NO_VALUE = [undefined, null, "", "N/A"];
console.log(BAD_STATES.includes(doc.inventory.enclosures[2].state)); // true
console.log(NO_VALUE.includes(doc.inventory.details.bandwidth)); // true - ""const BAD_STATES = ["degraded", "failed", "offline"];
const NO_VALUE = [undefined, null, "", "N/A"];
console.log(BAD_STATES.includes(doc.inventory.enclosures[2].state)); // true
console.log(NO_VALUE.includes(doc.inventory.details.bandwidth)); // true - ""indexOf(...) !== -1 includes се появи в ES2016. По-старите правила пишат същото с indexOf:
| По-стари правила | Модерно |
|---|---|
MODELS.indexOf(model) !== -1 | MODELS.includes(model) |
MODELS.indexOf(model) === -1 | !MODELS.includes(model) |
MODELS.indexOf(model) >= 0 | MODELS.includes(model) |
MODELS.indexOf(model) > 0 | бъг: пропуска индекс 0 |
["XR-210"].includes(m) !== true | !["XR-210"].includes(m) |
const MODELS = ["XR-210", "XR-220"];
console.log(MODELS.indexOf("XR-210") > 0); // false - but it IS in the list!
console.log(MODELS.includes("XR-210")); // trueconst MODELS = ["XR-210", "XR-220"];
console.log(MODELS.indexOf("XR-210") > 0); // false - but it IS in the list!
console.log(MODELS.includes("XR-210")); // trueindexOf и includes се различават само за NaN: [NaN].indexOf(NaN) е -1, [NaN].includes(NaN) е true.
const MODELS = ["XR-210", "XR-220"];
const fromReport = " xr-220";
console.log(MODELS.includes(fromReport)); // false
console.log(MODELS.includes(fromReport.trim().toUpperCase())); // trueconst MODELS = ["XR-210", "XR-220"];
const fromReport = " xr-220";
console.log(MODELS.includes(fromReport)); // false
console.log(MODELS.includes(fromReport.trim().toUpperCase())); // truetrim() премахва интервалите в двата края, toUpperCase() прави главни букви (повече в занятие 8).
indexOf върху обекти
indexOf и includes сравняват с ===, затова при обекти търсят същия обект, а не "обект със същите ключове": doc.inventory.enclosures.includes({ id: "ENC-01" }) е false. За да намерите запис по един от ключовете му, използвайте loop (раздел 10) - или find (занятие 7).
И strings имат indexOf и includes
"4.2.1.7-B00".includes("-"), "XR-220".indexOf("-") (дава 2), "4.2.1.7".at(-1) и "4.2.1.7".slice(0, 3) работят върху strings по същия начин. Повече в занятие 8.
Анимации
Методи на arrays: 3. indexOf и includes; Алгоритми с arrays: 6. Търсене с break (advanced) - какво прави indexOf отвътре
split и join split - от string към array str.split(separator) разрязва string-а на всеки разделител и връща array от парчетата. Разделителите отпадат. Парчетата винаги са strings.
console.log("4.2.1.7".split(".")); // [ '4', '2', '1', '7' ]
console.log("CON-01;CON-02".split(";")); // [ 'CON-01', 'CON-02' ]
console.log("XR-220".split("-")); // [ 'XR', '220' ]
console.log("4.2.1.7".split("-")); // [ '4.2.1.7' ] - no "-": one piece
console.log("abc".split("")); // [ 'a', 'b', 'c' ] - charactersconsole.log("4.2.1.7".split(".")); // [ '4', '2', '1', '7' ]
console.log("CON-01;CON-02".split(";")); // [ 'CON-01', 'CON-02' ]
console.log("XR-220".split("-")); // [ 'XR', '220' ]
console.log("4.2.1.7".split("-")); // [ '4.2.1.7' ] - no "-": one piece
console.log("abc".split("")); // [ 'a', 'b', 'c' ] - charactersТри употреби, които ще видите във всяко правило:
// 1. the first word: the version before " (build ...)"
const level = doc.inventory.details.firmware_level;
console.log(level.split(" ")[0]); // 4.2.1.7
// 2. the last part of an id: split, then take the last element
const alias = "lab-dev-01";
console.log(alias.split("-").pop()); // 01
console.log(alias.split("-").at(-1)); // 01 (ES2022)
// 3. a list packed into one string
const consoles = doc.inventory.details.consoles.split(";");
console.log(consoles, consoles.length); // [ 'CON-01', 'CON-02' ] 2// 1. the first word: the version before " (build ...)"
const level = doc.inventory.details.firmware_level;
console.log(level.split(" ")[0]); // 4.2.1.7
// 2. the last part of an id: split, then take the last element
const alias = "lab-dev-01";
console.log(alias.split("-").pop()); // 01
console.log(alias.split("-").at(-1)); // 01 (ES2022)
// 3. a list packed into one string
const consoles = doc.inventory.details.consoles.split(";");
console.log(consoles, consoles.length); // [ 'CON-01', 'CON-02' ] 2split дава strings, а strings се сравняват символ по символ - "10" < "9" е true (лекция 3). Преобразувайте всяка част с Number, преди да сравнявате:
const parts = "4.10.0.3".split(".");
console.log(parts); // [ '4', '10', '0', '3' ]
console.log(parts[1] < "9"); // true - wrong, string comparison
const numbers = [];
for (const part of parts) { // for...of: section 8
numbers.push(Number(part));
}
console.log(numbers); // [ 4, 10, 0, 3 ]
console.log(numbers[1] < 9); // false - rightconst parts = "4.10.0.3".split(".");
console.log(parts); // [ '4', '10', '0', '3' ]
console.log(parts[1] < "9"); // true - wrong, string comparison
const numbers = [];
for (const part of parts) { // for...of: section 8
numbers.push(Number(part));
}
console.log(numbers); // [ 4, 10, 0, 3 ]
console.log(numbers[1] < 9); // false - rightТова е първата стъпка на versionBelow - цялата функция ще напишете в занятие 5. (В занятие 7 loop-ът се свива до parts.map(Number).)
"key=value,key=value" Отчетите често пакетират няколко стойности в един string. Две разделяния ги разпакетират: първо по разделителя на списъка, после по разделителя ключ / стойност:
const settings = doc.inventory.details.settings; // "SA=2,SG=1,SN=2"
const pairs = settings.split(",");
console.log(pairs); // [ 'SA=2', 'SG=1', 'SN=2' ]
let sg = "";
for (const pair of pairs) {
const kv = pair.split("="); // [ 'SG', '1' ] - kv[0] key, kv[1] value
if (kv[0] === "SG") {
sg = kv[1];
break;
}
}
console.log(sg); // 1 - a string!const settings = doc.inventory.details.settings; // "SA=2,SG=1,SN=2"
const pairs = settings.split(",");
console.log(pairs); // [ 'SA=2', 'SG=1', 'SN=2' ]
let sg = "";
for (const pair of pairs) {
const kv = pair.split("="); // [ 'SG', '1' ] - kv[0] key, kv[1] value
if (kv[0] === "SG") {
sg = kv[1];
break;
}
}
console.log(sg); // 1 - a string!Стойност, която съдържа разделителя
"note=a=b".split("=") дава [ 'note', 'a', 'b' ] - стойността също се разрязва. Разделете само на първото = с indexOf и slice на string-а:
const pair = "note=a=b";
const pos = pair.indexOf("=");
console.log(pair.slice(0, pos)); // note
console.log(pair.slice(pos + 1)); // a=bconst pair = "note=a=b";
const pos = pair.indexOf("=");
console.log(pair.slice(0, pos)); // note
console.log(pair.slice(pos + 1)); // a=b(split има втори аргумент, ограничение: "note=a=b".split("=", 2) дава [ 'note', 'a' ] - останалото отпада, така че тук не помага.)
split console.log("".split(";")); // [ '' ] - ONE empty string, not []
console.log("".split(";").length); // 1 - "one console"?!
console.log("CON-01;".split(";")); // [ 'CON-01', '' ] - empty piece
console.log("CON-01; CON-02".split(";")); // [ 'CON-01', ' CON-02' ] - a spaceconsole.log("".split(";")); // [ '' ] - ONE empty string, not []
console.log("".split(";").length); // 1 - "one console"?!
console.log("CON-01;".split(";")); // [ 'CON-01', '' ] - empty piece
console.log("CON-01; CON-02".split(";")); // [ 'CON-01', ' CON-02' ] - a spaceПроверете string-а преди да го разрежете и почистете парчетата:
const raw = "CON-01; CON-02;";
const list = [];
if (raw !== "") {
for (const piece of raw.split(";")) {
const name = piece.trim(); // remove the spaces
if (name !== "") { // skip empty pieces
list.push(name);
}
}
}
console.log(list); // [ 'CON-01', 'CON-02' ]const raw = "CON-01; CON-02;";
const list = [];
if (raw !== "") {
for (const piece of raw.split(";")) {
const name = piece.trim(); // remove the spaces
if (name !== "") { // skip empty pieces
list.push(name);
}
}
}
console.log(list); // [ 'CON-01', 'CON-02' ]join - от array към string arr.join(separator) е обратното на split: слепва елементите в един string, с разделителя между тях. Без аргумент разделителят е ",".
const parts = ["4", "2", "1", "7"];
console.log(parts.join(".")); // 4.2.1.7
console.log(parts.join("")); // 4217
console.log(parts.join()); // 4,2,1,7
console.log([].join(", ")); // "" - an empty string
console.log(String(parts)); // 4,2,1,7 - the same as join()const parts = ["4", "2", "1", "7"];
console.log(parts.join(".")); // 4.2.1.7
console.log(parts.join("")); // 4217
console.log(parts.join()); // 4,2,1,7
console.log([].join(", ")); // "" - an empty string
console.log(String(parts)); // 4,2,1,7 - the same as join()join строи текстовете на alerts. Събирайте частите с push и ги слепете веднъж накрая:
const findingParts = [];
const actionParts = [];
findingParts.push("Firmware 4.1.9.3 is below the minimum 4.2.0.0.");
actionParts.push("Upgrade as soon as possible.");
findingParts.push("The authentication service has no password.");
actionParts.push("Set a password for the service account.");
const details = {
"Description": findingParts.join(" "),
"Action": actionParts.join(" ")
};
console.log(details.Description);
console.log(details.Action);const findingParts = [];
const actionParts = [];
findingParts.push("Firmware 4.1.9.3 is below the minimum 4.2.0.0.");
actionParts.push("Upgrade as soon as possible.");
findingParts.push("The authentication service has no password.");
actionParts.push("Set a password for the service account.");
const details = {
"Description": findingParts.join(" "),
"Action": actionParts.join(" ")
};
console.log(details.Description);
console.log(details.Action);Така избягвате класическия loop text = text + ", " + item, който оставя излишно ", " в началото или в края.
Анимации
Методи на arrays: 4. split и join, 5. Пакетирани стойности
Често трябва да направим едно и също с всеки елемент. Класическият начин е for loop по индексите, от 0 до length - 1:
let fruits = ["apple", "banana", "strawberry"];
for (let i = 0; i < fruits.length; i++) {
console.log(`Element ${i}: ${fruits[i]}`);
}
// Element 0: apple
// Element 1: banana
// Element 2: strawberrylet fruits = ["apple", "banana", "strawberry"];
for (let i = 0; i < fruits.length; i++) {
console.log(`Element ${i}: ${fruits[i]}`);
}
// Element 0: apple
// Element 1: banana
// Element 2: strawberryПишете i < arr.length, а не i <= arr.length: последният индекс е length - 1, а arr[length] е undefined.
Анимация
Алгоритми с arrays: 1. Обхождане с for
Започнете от последния индекс, продължете докато i >= 0, стъпка i--:
let fruits = ["apple", "banana", "strawberry"];
for (let i = fruits.length - 1; i >= 0; i--) {
console.log(`Element ${i}: ${fruits[i]}`);
}
// Element 2: strawberry
// Element 1: banana
// Element 0: applelet fruits = ["apple", "banana", "strawberry"];
for (let i = fruits.length - 1; i >= 0; i--) {
console.log(`Element ${i}: ${fruits[i]}`);
}
// Element 2: strawberry
// Element 1: banana
// Element 0: appleНай-новият запис в списък с история често е последният - обхождайте назад и спрете с break при първото съвпадение.
Анимация
Алгоритми с arrays: 2. Обратен ред
for...of - елементите, без индекса MDN: for...of
for...of (ES2015) дава елементите един по един. Използвайте го, когато не ви трябва индексът - това е в повечето случаи:
for (const feature of doc.inventory.features) {
console.log(feature);
}
// with the index too: entries() gives [index, element] pairs
for (const [i, feature] of doc.inventory.features.entries()) {
console.log(i, feature);
}for (const feature of doc.inventory.features) {
console.log(feature);
}
// with the index too: entries() gives [index, element] pairs
for (const [i, feature] of doc.inventory.features.entries()) {
console.log(i, feature);
}const тук е наред: всяко завъртане получава нова variable feature. ([i, feature] е destructuring - повече в занятие 6.)
| Използвайте | Кога |
|---|---|
for (const x of arr) | трябват ви само елементите - по подразбиране |
for (let i = 0; i < arr.length; i++) | трябва ви индексът, стъпка, различна от 1, или два arrays с един и същ индекс |
for (let i = arr.length - 1; i >= 0; i--) | назад или когато премахвате елементи по време на обхождането |
for...in не е за arrays
for (const i in arr) обхожда ключовете: те са strings ("0", "1", ...), и идват и допълнителните properties (arr.a от раздел 4). По-старите правила го използват върху arrays - работи за обикновени arrays, но i + 1 дава "01", а не 1! Използвайте for...in за обекти (занятие 6), а for...of или for - за arrays.
const arr = ["a", "b"];
for (const i in arr) {
console.log(i, typeof i, i + 1); // 0 string 01, then 1 string 11
}const arr = ["a", "b"];
for (const i in arr) {
console.log(i, typeof i, i + 1); // 0 string 01, then 1 string 11
}arr.forEach(function (x) { ... }) - още един loop, който ще видите в правилата - приема функция, затова идва в занятия 5 и 7.
Пример с бъг. Изпълнете го - кой ред е грешен?
const numbers = [1, 2, 3, 4, 5];
console.log("Every element of the array:");
for (let i = 0; i < numbers.length; i++) {
console.log("\t" + numbers[i]);
}
console.log("Every element, to the power of 2:");
for (let i = 0; i < numbers.length; i++) {
console.log("\t" + numbers[i] ** 2);
}
console.log("The sum of the elements:");
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += i;
}
console.log("\t" + sum); // 10?! 1 + 2 + 3 + 4 + 5 is 15const numbers = [1, 2, 3, 4, 5];
console.log("Every element of the array:");
for (let i = 0; i < numbers.length; i++) {
console.log("\t" + numbers[i]);
}
console.log("Every element, to the power of 2:");
for (let i = 0; i < numbers.length; i++) {
console.log("\t" + numbers[i] ** 2);
}
console.log("The sum of the elements:");
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += i;
}
console.log("\t" + sum); // 10?! 1 + 2 + 3 + 4 + 5 is 15sum += i добавя индекса, а не елемента: 0 + 1 + 2 + 3 + 4 = 10. Трябва да е sum += numbers[i]. С for...of този бъг не може да се случи: for (const n of numbers) { sum += n; }.
Една variable събира резултата, докато loop-ът върви. Дайте ѝ правилната начална стойност преди loop-а: 0 за сума или брой, "" за текст, [] за списък.
// the sum of all numbers from 1 to 10
let sum = 0;
for (let x = 1; x <= 10; x++) {
sum += x;
}
console.log("sum = " + sum); // 55
// the same idea on the report: count the enclosures in a state
let normal = 0;
for (const enc of doc.inventory.enclosures) {
if (enc.state === "normal") {
normal++;
}
}
const total = doc.inventory.enclosures.length;
console.log(`${normal} of ${total} enclosures are normal`);// the sum of all numbers from 1 to 10
let sum = 0;
for (let x = 1; x <= 10; x++) {
sum += x;
}
console.log("sum = " + sum); // 55
// the same idea on the report: count the enclosures in a state
let normal = 0;
for (const enc of doc.inventory.enclosures) {
if (enc.state === "normal") {
normal++;
}
}
const total = doc.inventory.enclosures.length;
console.log(`${normal} of ${total} enclosures are normal`);Проверете се: отпечатайте сумата на всички положителни четни числа в [-2, 3, 7, 8, -1, 4, 2]. (Очаквано: 14.)
const numbers = [-2, 3, 7, 8, -1, 4, 2];
let sum = 0;
for (const n of numbers) {
if (n > 0 && n % 2 === 0) {
sum += n;
}
}
console.log(sum); // 8 + 4 + 2 = 14Същото за диапазон от числа, без array: сумата от 1 до 100 и нечетните числа под 100, които се делят на 11:
let total = 0;
for (let i = 1; i <= 100; i++) {
total += i;
}
console.log("sum:", total); // 5050
const odd11 = [];
for (let i = 1; i < 100; i++) {
if (i % 2 !== 0 && i % 11 === 0) {
odd11.push(i);
}
}
console.log(odd11); // [ 11, 33, 55, 77, 99 ]let total = 0;
for (let i = 1; i <= 100; i++) {
total += i;
}
console.log("sum:", total); // 5050
const odd11 = [];
for (let i = 1; i < 100; i++) {
if (i % 2 !== 0 && i % 11 === 0) {
odd11.push(i);
}
}
console.log(odd11); // [ 11, 33, 55, 77, 99 ]Анимация
Алгоритми с arrays: 3. Сума на елементите
Четните числа в array:
const numbers = [2, 3, 5, 8, 5];
for (const n of numbers) {
if (n % 2 === 0) {
console.log(n); // 2, 8
}
}const numbers = [2, 3, 5, 8, 5];
for (const n of numbers) {
if (n % 2 === 0) {
console.log(n); // 2, 8
}
}Отрицателните нечетни числа:
const numArray = [-1, -2, -3, -4, -5, 0, 1, 2, 3, 4, 5];
console.log("The negative odd array elements are:");
for (const n of numArray) {
if (n < 0 && n % 2 !== 0) {
console.log(n); // -1, -3, -5
}
}const numArray = [-1, -2, -3, -4, -5, 0, 1, 2, 3, 4, 5];
console.log("The negative odd array elements are:");
for (const n of numArray) {
if (n < 0 && n % 2 !== 0) {
console.log(n); // -1, -3, -5
}
}Две задачи в един loop: нечетните числа и нечетните положителни числа:
const a = [0, 2, 3, 41, 5, 60, 7, 80];
for (const n of a) {
if (n % 2 !== 0) console.log(n); // 3, 41, 5, 7
}
const b = [0, 2, -3, 41, -5, -60, 7, 11, -80];
for (const n of b) {
if (n > 0 && n % 2 !== 0) console.log(n); // 41, 7, 11
}const a = [0, 2, 3, 41, 5, 60, 7, 80];
for (const n of a) {
if (n % 2 !== 0) console.log(n); // 3, 41, 5, 7
}
const b = [0, 2, -3, 41, -5, -60, 7, 11, -80];
for (const n of b) {
if (n > 0 && n % 2 !== 0) console.log(n); // 41, 7, 11
}Вместо да отпечатвате, съберете съвпадащите елементи в нов array с push. Задачата separateArrayElementsInNewArrays: положителните четни числа отиват в един array, отрицателните нечетни - в друг (нулата не е нито положителна, нито отрицателна):
const numbersArr = [1, 6, -3, 5, 2, -8, 0, 4, 3];
const evenNums = [];
const oddNums = [];
for (const n of numbersArr) {
if (n > 0 && n % 2 === 0) {
evenNums.push(n);
} else if (n < 0 && n % 2 !== 0) {
oddNums.push(n);
}
}
console.log(`evenNums: ${evenNums}`); // 6,2,4
console.log(`oddNums: ${oddNums}`); // -3const numbersArr = [1, 6, -3, 5, 2, -8, 0, 4, 3];
const evenNums = [];
const oddNums = [];
for (const n of numbersArr) {
if (n > 0 && n % 2 === 0) {
evenNums.push(n);
} else if (n < 0 && n % 2 !== 0) {
oddNums.push(n);
}
}
console.log(`evenNums: ${evenNums}`); // 6,2,4
console.log(`oddNums: ${oddNums}`); // -3С отчета - id-тата на enclosures, които не са на firmware "2.14":
const outdated = [];
for (const enc of doc.inventory.enclosures) {
if (enc.firmware !== "2.14") {
outdated.push(enc.id);
}
}
console.log(outdated); // [ 'ENC-02', 'ENC-04' ]
console.log(outdated.join(", ")); // ENC-02, ENC-04const outdated = [];
for (const enc of doc.inventory.enclosures) {
if (enc.firmware !== "2.14") {
outdated.push(enc.id);
}
}
console.log(outdated); // [ 'ENC-02', 'ENC-04' ]
console.log(outdated.join(", ")); // ENC-02, ENC-04Анимация
Алгоритми с arrays: 5. Филтриране в нови arrays
Вземете първия елемент за най-голям засега, после го сравнете с всеки следващ:
const temps = [31, 34, 37, 29, 38, 30];
let max = temps[0];
let maxIndex = 0;
for (let i = 1; i < temps.length; i++) {
if (temps[i] > max) {
max = temps[i];
maxIndex = i;
}
}
console.log(`max = ${max}, index = ${maxIndex}`); // max = 38, index = 4const temps = [31, 34, 37, 29, 38, 30];
let max = temps[0];
let maxIndex = 0;
for (let i = 1; i < temps.length; i++) {
if (temps[i] > max) {
max = temps[i];
maxIndex = i;
}
}
console.log(`max = ${max}, index = ${maxIndex}`); // max = 38, index = 4Не започвайте от 0
let max = 0; изглежда безобидно, но само с отрицателни числа резултатът е 0 - стойност, която я няма в array-я. Започнете от arr[0]. И първо проверете, че array-ят не е празен: [][0] е undefined.
Math.max(...temps) дава същия максимум на един ред (spread подава елементите като отделни аргументи; Math в занятие 8) - но не и индекса.
Анимация
Алгоритми с arrays: 4. Най-голям елемент
break Когато ви трябва един елемент, спрете при първото съвпадение:
const ids = ["ENC-01", "ENC-02", "ENC-03", "ENC-04"];
let index = -1; // -1 = "not found"
for (let i = 0; i < ids.length; i++) {
if (ids[i] === "ENC-03") {
index = i;
break; // do not look further
}
}
console.log(index); // 2const ids = ["ENC-01", "ENC-02", "ENC-03", "ENC-04"];
let index = -1; // -1 = "not found"
for (let i = 0; i < ids.length; i++) {
if (ids[i] === "ENC-03") {
index = i;
break; // do not look further
}
}
console.log(index); // 2За обикновена стойност това е просто ids.indexOf("ENC-03"). Loop пишете, когато условието е нещо повече от === - например "първият enclosure, който не е normal" (раздел 10).
% В уеб страница един бутон може да сменя цвета на една кутия при всяко щракване, като обикаля списък с цветове. Без браузъра идеята е индексът с % length:
const colors = ["blue", "purple", "red"];
let i = 0;
for (let click = 1; click <= 7; click++) {
console.log(`click ${click}: ${colors[i]}`);
i = (i + 1) % colors.length; // 0, 1, 2, 0, 1, 2, ...
}const colors = ["blue", "purple", "red"];
let i = 0;
for (let click = 1; click <= 7; click++) {
console.log(`click ${click}: ${colors[i]}`);
i = (i + 1) % colors.length; // 0, 1, 2, 0, 1, 2, ...
}Така се разпределят проверки върху списък от конзоли, една след друга (round robin).
В JavaScript няма истински двумерни (или многомерни) arrays. Но елементът може да е от всеки тип - също array - затова таблицата е array от arrays: всеки елемент е ред, а всеки ред е array.
const matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
console.log("matrix:", matrix);const matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
console.log("matrix:", matrix);const matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
console.log("row 0, col 2:", matrix[0][2]); // c
console.log("row 1, col 1:", matrix[1][1]); // e
console.log("row 2, col 0:", matrix[2][0]); // g
console.log(matrix.length, matrix[0].length); // 3 3 - rows, columnsconst matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
console.log("row 0, col 2:", matrix[0][2]); // c
console.log("row 1, col 1:", matrix[1][1]); // e
console.log("row 2, col 0:", matrix[2][0]); // g
console.log(matrix.length, matrix[0].length); // 3 3 - rows, columnsmatrix[1][1] се чете на две стъпки: matrix[1] дава реда ["d", "e", "f"], а [1] от този ред дава "e".
Анимации
Arrays в паметта: 6. Двумерен array (advanced), 7. Общ ред (advanced) - защо [row, row] е капан
Два вложени loops: външният по редовете (i), вътрешният по колоните на текущия ред (j):
const matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
const rows = matrix.length;
const cols = matrix[0].length;
for (let i = 0; i < rows; i++) {
console.log("row:", i);
for (let j = 0; j < cols; j++) {
console.log(" " + matrix[i][j]);
}
}const matrix = [
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"],
];
const rows = matrix.length;
const cols = matrix[0].length;
for (let i = 0; i < rows; i++) {
console.log("row:", i);
for (let j = 0; j < cols; j++) {
console.log(" " + matrix[i][j]);
}
}С for...of същото е по-кратко - и работи, когато редовете са с различна дължина:
const matrix = [["a", "b", "c"], ["d", "e"], ["f"]];
for (const row of matrix) {
console.log(row.join(" | "));
}const matrix = [["a", "b", "c"], ["d", "e"], ["f"]];
for (const row of matrix) {
console.log(row.join(" | "));
}Сумата на главния диагонал (m[0][0] + m[1][1] + m[2][2]) иска само един loop:
const m = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let sum = 0;
for (let i = 0; i < m.length; i++) {
sum += m[i][i];
}
console.log(`sum = ${sum}`); // 1 + 5 + 9 = 15const m = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let sum = 0;
for (let i = 0; i < m.length; i++) {
sum += m[i][i];
}
console.log(`sum = ${sum}`); // 1 + 5 + 9 = 15Анимация
Алгоритми с arrays: 7. Двумерен array (advanced)
Проверете се: fruitsData пази имената в ред 0, а цените в ред 1. Отпечатайте apple = 1.2, orange = 2, cherry = 3.5 с loop.
const fruitsData = [
["apple", "orange", "cherry"],
[1.2, 2.0, 3.5],
];
// the long way:
// console.log(fruitsData[0][0] + " = " + fruitsData[1][0]);
// console.log(fruitsData[0][1] + " = " + fruitsData[1][1]);
// console.log(fruitsData[0][2] + " = " + fruitsData[1][2]);
// your loop here:const fruitsData = [
["apple", "orange", "cherry"],
[1.2, 2.0, 3.5],
];
// the long way:
// console.log(fruitsData[0][0] + " = " + fruitsData[1][0]);
// console.log(fruitsData[0][1] + " = " + fruitsData[1][1]);
// console.log(fruitsData[0][2] + " = " + fruitsData[1][2]);
// your loop here:Името и цената на един плод са в една и съща колона j, затова обхождайте колоните:
for (let j = 0; j < fruitsData[0].length; j++) {
console.log(fruitsData[0][j] + " = " + fruitsData[1][j]);
}2.0 се отпечатва като 2: за JavaScript 2.0 и 2 са едно и също число.
Отчетите понякога изпращат таблица като редове от стойности, а имената на колоните - в отделен списък, като първия ред на CSV файл например:
const header = ["id", "temperature"];
const rows = [
["ENC-01", "31"],
["ENC-02", "34"],
["ENC-03", "38"],
];
const col = header.indexOf("temperature"); // find the column by its name
for (const row of rows) {
if (Number(row[col]) > 35) {
console.log(row[0] + " is too hot: " + row[col] + " C");
}
}const header = ["id", "temperature"];
const rows = [
["ENC-01", "31"],
["ENC-02", "34"],
["ENC-03", "38"],
];
const col = header.indexOf("temperature"); // find the column by its name
for (const row of rows) {
if (Number(row[col]) > 35) {
console.log(row[0] + " is too hot: " + row[col] + " C");
}
}header.indexOf(...) вместо фиксирано row[1]: кодът продължава да работи, ако експортът добави колона отпред.
Записът е група от стойности за едно нещо - един enclosure, един диск, една настройка. В JSON записът е обект { key: value, ... } (обектите подробно: занятие 6). Списъкът от записи е array от такива обекти, всички с едни и същи ключове:
const enclosures = doc.inventory.enclosures;
console.log(enclosures.length); // 4
console.log(enclosures[0]); // the whole first record: an object
console.log(enclosures[2].state); // degraded - index first, then the key
for (const enc of enclosures) {
console.log(enc.id, enc.type, enc.firmware, enc.state);
}const enclosures = doc.inventory.enclosures;
console.log(enclosures.length); // 4
console.log(enclosures[0]); // the whole first record: an object
console.log(enclosures[2].state); // degraded - index first, then the key
for (const enc of enclosures) {
console.log(enc.id, enc.type, enc.firmware, enc.state);
}По-старият код често пази записа като array:
// [gender, age, first name, last name]
const students = [
["male", 24, "Ivan", "Ivanov"],
["female", 22, "Maria", "Petrova"],
["male", 18, "Georgi", "Georgiev"],
["male", 21, "Nikolai", "Nikolov"],
];
// the youngest student: start with the first one, compare with the rest
let youngest = students[0];
for (const student of students) {
if (student[1] < youngest[1]) {
youngest = student;
}
}
console.log(`The youngest: ${youngest[2]} ${youngest[3]}, ${youngest[1]} years old.`);// [gender, age, first name, last name]
const students = [
["male", 24, "Ivan", "Ivanov"],
["female", 22, "Maria", "Petrova"],
["male", 18, "Georgi", "Georgiev"],
["male", 21, "Nikolai", "Nikolov"],
];
// the youngest student: start with the first one, compare with the rest
let youngest = students[0];
for (const student of students) {
if (student[1] < youngest[1]) {
youngest = student;
}
}
console.log(`The youngest: ${youngest[2]} ${youngest[3]}, ${youngest[1]} years old.`);Работи, но какво е student[1]? Трябва да помните, че индекс 1 е възрастта. Обектът го казва: student.age. Затова JSON отчетите използват обекти за записите.
Проверете се: намерете най-възрастния студент. (Очаквано: Ivan Ivanov, 24.)
Същият loop с > вместо <: if (student[1] > oldest[1]) { oldest = student; }, като започнете с let oldest = students[0];. Честа грешка е да се започне с minAge = students[0][1] и да се остави youngestStudent недефинирана - ако първият студент е най-младият, програмата гърми. Започването с целия първи запис избягва това.
Задача в същата форма - владетели и годините на управлението им. Имената са с разбъркани малки и главни букви, затова една малка helper функция ги оправя:
const rulers = [
["asPARuH", 681, 700],
["tERveL", 700, 718],
["SeVaR", 738, 753],
["telERIG", 766, 777],
["kaRDAm", 777, 803],
["krum", 803, 814],
["OmurTAg", 814, 831],
];
// ready-made: first letter upper case, the rest lower case (session 8)
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1).toLowerCase();
}
// the rulers who ruled more than 15 years
for (const ruler of rulers) {
const years = ruler[2] - ruler[1];
if (years > 15) {
console.log(`${capitalize(ruler[0])} ruled ${years} years.`);
}
}const rulers = [
["asPARuH", 681, 700],
["tERveL", 700, 718],
["SeVaR", 738, 753],
["telERIG", 766, 777],
["kaRDAm", 777, 803],
["krum", 803, 814],
["OmurTAg", 814, 831],
];
// ready-made: first letter upper case, the rest lower case (session 8)
function capitalize(str) {
return str[0].toUpperCase() + str.slice(1).toLowerCase();
}
// the rulers who ruled more than 15 years
for (const ruler of rulers) {
const years = ruler[2] - ruler[1];
if (years > 15) {
console.log(`${capitalize(ruler[0])} ruled ${years} years.`);
}
}Три въпроса, които задавате на един списък от записи отново и отново:
const enclosures = doc.inventory.enclosures;
// FIND: the first record that matches - stop with break
let firstBad; // undefined = not found
for (const enc of enclosures) {
if (enc.state !== "normal") {
firstBad = enc;
break;
}
}
console.log(firstBad ? firstBad.id : "all normal"); // ENC-03
// COUNT: how many records match
let hddCount = 0;
for (const enc of enclosures) {
if (enc.type.startsWith("HDD")) {
hddCount++;
}
}
console.log(hddCount); // 2
// COLLECT: a value from every matching record
const oldFirmware = [];
for (const enc of enclosures) {
if (enc.firmware !== "2.14") {
oldFirmware.push(`${enc.id} (${enc.firmware})`);
}
}
console.log(oldFirmware.join(", ")); // ENC-02 (2.10), ENC-04 (2.10)const enclosures = doc.inventory.enclosures;
// FIND: the first record that matches - stop with break
let firstBad; // undefined = not found
for (const enc of enclosures) {
if (enc.state !== "normal") {
firstBad = enc;
break;
}
}
console.log(firstBad ? firstBad.id : "all normal"); // ENC-03
// COUNT: how many records match
let hddCount = 0;
for (const enc of enclosures) {
if (enc.type.startsWith("HDD")) {
hddCount++;
}
}
console.log(hddCount); // 2
// COLLECT: a value from every matching record
const oldFirmware = [];
for (const enc of enclosures) {
if (enc.firmware !== "2.14") {
oldFirmware.push(`${enc.id} (${enc.firmware})`);
}
}
console.log(oldFirmware.join(", ")); // ENC-02 (2.10), ENC-04 (2.10)startsWith проверява началото на string - занятие 8. В занятие 7 тези три loops стават find, filter(...).length и filter + map.
Анимация
Методи на arrays: 6. Списък от записи
Някои експорти изобщо не изпращат вложен обект. Всяка стойност е малък запис, а списъкът има хиляди такива:
const records = [
{ group: "Global Resources", item: "SYS", code: "BU", label: "Release", value: "10.12.39.0" },
{ group: "Global Resources", item: "SYS", code: "FN", label: "Family", value: "AR9" },
{ group: "Enclosures", item: "ENC-01", code: "FW", label: "Firmware", value: "2.14" },
{ group: "Enclosures", item: "ENC-01", code: "ST", label: "State", value: "normal" },
{ group: "Enclosures", item: "ENC-02", code: "FW", label: "Firmware", value: "2.10" },
{ group: "Enclosures", item: "ENC-02", code: "ST", label: "State", value: "degraded" },
{ group: "Partitions", item: "P1", code: "SN", label: "Settings", value: "SA=2,SG=2,SN=2" },
];
// one value: the record with item "SYS" and code "BU"
let release;
for (const r of records) {
if (r.item === "SYS" && r.code === "BU") {
release = r.value;
break;
}
}
console.log(release); // 10.12.39.0
// all values of one kind: the firmware of every enclosure
const firmware = [];
for (const r of records) {
if (r.group === "Enclosures" && r.code === "FW") {
firmware.push(r.item + "=" + r.value);
}
}
console.log(firmware); // [ 'ENC-01=2.14', 'ENC-02=2.10' ]const records = [
{ group: "Global Resources", item: "SYS", code: "BU", label: "Release", value: "10.12.39.0" },
{ group: "Global Resources", item: "SYS", code: "FN", label: "Family", value: "AR9" },
{ group: "Enclosures", item: "ENC-01", code: "FW", label: "Firmware", value: "2.14" },
{ group: "Enclosures", item: "ENC-01", code: "ST", label: "State", value: "normal" },
{ group: "Enclosures", item: "ENC-02", code: "FW", label: "Firmware", value: "2.10" },
{ group: "Enclosures", item: "ENC-02", code: "ST", label: "State", value: "degraded" },
{ group: "Partitions", item: "P1", code: "SN", label: "Settings", value: "SA=2,SG=2,SN=2" },
];
// one value: the record with item "SYS" and code "BU"
let release;
for (const r of records) {
if (r.item === "SYS" && r.code === "BU") {
release = r.value;
break;
}
}
console.log(release); // 10.12.39.0
// all values of one kind: the firmware of every enclosure
const firmware = [];
for (const r of records) {
if (r.group === "Enclosures" && r.code === "FW") {
firmware.push(r.item + "=" + r.value);
}
}
console.log(firmware); // [ 'ENC-01=2.14', 'ENC-02=2.10' ]Папката с домашните има цял експорт в тази форма: Homework/data/report-records.json (79 записа). Задачите за упражнение го използват.
Хиляди записи: не търсете в loop
За да намерите една стойност, loop обхожда списъка - веднъж е наред. Но "за всеки enclosure намери състоянието му", написано като loop в loop, обхожда целия списък отново за всеки enclosure: с 9500 записа и 100 enclosures това са почти милион сравнения. Обходете списъка веднъж и съберете каквото ви трябва. В занятие 7 ще построим индекс (обект или Map с ключ item) веднъж и след това всяко търсене е мигновено.
Едни и същи данни могат да дойдат като списък в един отчет и като единичен обект в друг (например когато има само един enclosure). Първо ги нормализирайте до array, тогава останалата част от кода има само една форма:
function asList(value) {
if (Array.isArray(value)) return value; // already a list
if (value === undefined || value === null) return []; // nothing
return [value]; // one value -> a list of one
}
console.log(asList(doc.inventory.enclosures).length); // 4
console.log(asList({ id: "ENC-09", state: "normal" })); // a list of one
console.log(asList(undefined)); // []function asList(value) {
if (Array.isArray(value)) return value; // already a list
if (value === undefined || value === null) return []; // nothing
return [value]; // one value -> a list of one
}
console.log(asList(doc.inventory.enclosures).length); // 4
console.log(asList({ id: "ENC-09", state: "normal" })); // a list of one
console.log(asList(undefined)); // [](function с return е тема на занятие 5 - четете го като "върни тази стойност".)
Първо изпълнете правилото от началото на лекцията. emit тук е фалшив: само отпечатва alert-а.
function emit(id, details) { // fake: prints instead of creating an alert
console.log("ALERT for " + id + ": " + JSON.stringify(details));
}
// ready-made helper - we write our own in session 5
function versionBelow(a, b) {
const pa = String(a).split(".").map(Number);
const pb = String(b).split(".").map(Number);
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const x = pa[i] || 0;
const y = pb[i] || 0;
if (x !== y) return x < y;
}
return false;
}
const MODELS = ["XR-210", "XR-220"];
function rule(doc) {
if (!doc.overview || !doc.inventory?.details) return;
const model = doc.overview.model;
if (!MODELS.includes(model)) return;
const level = doc.inventory.details.firmware_level ?? "";
const version = level.split(" ")[0];
const enclosures = doc.inventory.enclosures;
if (!Array.isArray(enclosures)) return;
const problems = [];
for (const enc of enclosures) {
if (enc.state !== "normal") {
problems.push(`${enc.id} is ${enc.state}`);
}
if (versionBelow(enc.firmware, "2.14")) {
problems.push(`${enc.id} has firmware ${enc.firmware}`);
}
}
if (problems.length === 0) return;
const name = doc.inventory.extended_info?.json?.data?.system?.name
|| doc.status.device_alias;
emit(name, {
"Model": model,
"Version": version,
"Problems": problems.length,
"Details": problems.join("; ")
});
}
console.log("example JSON document:");
rule(doc);
console.log("all enclosures fine:");
rule({
overview: { model: "XR-210" },
inventory: {
details: { firmware_level: "4.2.1.7 (build 412.17)" },
enclosures: [{ id: "ENC-01", firmware: "2.14", state: "normal" }]
},
status: { device_alias: "LAB-DEV-02" }
});function emit(id, details) { // fake: prints instead of creating an alert
console.log("ALERT for " + id + ": " + JSON.stringify(details));
}
// ready-made helper - we write our own in session 5
function versionBelow(a, b) {
const pa = String(a).split(".").map(Number);
const pb = String(b).split(".").map(Number);
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const x = pa[i] || 0;
const y = pb[i] || 0;
if (x !== y) return x < y;
}
return false;
}
const MODELS = ["XR-210", "XR-220"];
function rule(doc) {
if (!doc.overview || !doc.inventory?.details) return;
const model = doc.overview.model;
if (!MODELS.includes(model)) return;
const level = doc.inventory.details.firmware_level ?? "";
const version = level.split(" ")[0];
const enclosures = doc.inventory.enclosures;
if (!Array.isArray(enclosures)) return;
const problems = [];
for (const enc of enclosures) {
if (enc.state !== "normal") {
problems.push(`${enc.id} is ${enc.state}`);
}
if (versionBelow(enc.firmware, "2.14")) {
problems.push(`${enc.id} has firmware ${enc.firmware}`);
}
}
if (problems.length === 0) return;
const name = doc.inventory.extended_info?.json?.data?.system?.name
|| doc.status.device_alias;
emit(name, {
"Model": model,
"Version": version,
"Problems": problems.length,
"Details": problems.join("; ")
});
}
console.log("example JSON document:");
rule(doc);
console.log("all enclosures fine:");
rule({
overview: { model: "XR-210" },
inventory: {
details: { firmware_level: "4.2.1.7 (build 412.17)" },
enclosures: [{ id: "ENC-01", firmware: "2.14", state: "normal" }]
},
status: { device_alias: "LAB-DEV-02" }
});Сменете състоянието на ENC-01 във втория документ на "failed" и изпълнете отново. После сменете enclosures на един обект вместо списък (махнете [ ]). Какво става и защо?
Ето същото правило в по-стария стил. Прочетете го спокойно и решете какво да модернизирате:
function emit(id, details) {
console.log("ALERT for " + id + ": " + JSON.stringify(details));
}
function versionBelow(a, b) {
var pa = String(a).split(".");
var pb = String(b).split(".");
for (var i = 0; i < Math.max(pa.length, pb.length); i++) {
var x = parseInt(pa[i], 10) || 0;
var y = parseInt(pb[i], 10) || 0;
if (x < y) return true;
if (x > y) return false;
}
return false;
}
var MODELS = new Array("XR-210", "XR-220");
function rule(doc) { // the engine calls rule(doc) - keep this line as it is
if (!doc.overview || !doc.inventory || !doc.inventory.details) return;
var model = doc.overview.model;
if (MODELS.indexOf(model) == -1) return;
var level = String(doc.inventory.details.firmware_level || "");
var version = level.split(" ")[0];
var enclosures = doc.inventory.enclosures;
if (!enclosures || !enclosures.length) return;
var problems = new Array();
for (var i = 0; i < enclosures.length; i++) {
var enc = enclosures[i];
if (enc.state != "normal") {
problems.push(enc.id + " is " + enc.state);
}
if (versionBelow(enc.firmware, "2.14") == true) {
problems.push(enc.id + " has firmware " + enc.firmware);
}
}
if (problems.length == 0) return;
var info = doc.inventory.extended_info;
var name = info && info.json && info.json.data && info.json.data.system
&& info.json.data.system.name || doc.status.device_alias;
var details = new Object();
details["Model"] = model;
details["Version"] = version;
details["Problems"] = problems.length;
details["Details"] = problems.join("; ");
emit(name, details);
}
console.log("example JSON document:");
rule(doc);
console.log("enclosures sent as a string:");
rule({
overview: { model: "XR-210" },
inventory: { details: { firmware_level: "4.2.1.7" }, enclosures: "ENC-01" },
status: { device_alias: "LAB-DEV-03" }
});function emit(id, details) {
console.log("ALERT for " + id + ": " + JSON.stringify(details));
}
function versionBelow(a, b) {
var pa = String(a).split(".");
var pb = String(b).split(".");
for (var i = 0; i < Math.max(pa.length, pb.length); i++) {
var x = parseInt(pa[i], 10) || 0;
var y = parseInt(pb[i], 10) || 0;
if (x < y) return true;
if (x > y) return false;
}
return false;
}
var MODELS = new Array("XR-210", "XR-220");
function rule(doc) { // the engine calls rule(doc) - keep this line as it is
if (!doc.overview || !doc.inventory || !doc.inventory.details) return;
var model = doc.overview.model;
if (MODELS.indexOf(model) == -1) return;
var level = String(doc.inventory.details.firmware_level || "");
var version = level.split(" ")[0];
var enclosures = doc.inventory.enclosures;
if (!enclosures || !enclosures.length) return;
var problems = new Array();
for (var i = 0; i < enclosures.length; i++) {
var enc = enclosures[i];
if (enc.state != "normal") {
problems.push(enc.id + " is " + enc.state);
}
if (versionBelow(enc.firmware, "2.14") == true) {
problems.push(enc.id + " has firmware " + enc.firmware);
}
}
if (problems.length == 0) return;
var info = doc.inventory.extended_info;
var name = info && info.json && info.json.data && info.json.data.system
&& info.json.data.system.name || doc.status.device_alias;
var details = new Object();
details["Model"] = model;
details["Version"] = version;
details["Problems"] = problems.length;
details["Details"] = problems.join("; ");
emit(name, details);
}
console.log("example JSON document:");
rule(doc);
console.log("enclosures sent as a string:");
rule({
overview: { model: "XR-210" },
inventory: { details: { firmware_level: "4.2.1.7" }, enclosures: "ENC-01" },
status: { device_alias: "LAB-DEV-03" }
});Проверете се:
new Array("XR-210", "XR-220") създава същия списък като ["XR-210", "XR-220"]. Какво би създало new Array(2)? Защо [...] е по-сигурно?enclosures като string. Какво прави старото правило с него и какво би направило модерното?if (!enclosures || !enclosures.length) return; и if (!Array.isArray(enclosures)) return; - еднакво ли се държат за []? За undefined? За един обект { id: "ENC-01", ... }?firmware_level: "4.2.1.7" - без частта (build ...). Каква е version в двете правила?var -> const / let; new Array(...) -> [...]; indexOf(model) == -1 -> !includes(model) (същото за strings); == / != -> === / !==; for с индекс -> for...of; == true -> нищо; + -> template strings; веригата с && -> ?.; new Object() + details["..."] -> обектен литерал. Биха могли да променят резултата: guard-ът за enclosures (въпроси 3 и 4) и String(... || "") срещу ?? "".[2]. [...] винаги означава "тези елементи".length (6), затова старият guard го пропуска и loop-ът обхожда символите: enc е "E", "N", ... enc.state и enc.firmware са undefined, така че всеки символ дава два "проблема" (versionBelow(undefined, "2.14") също е true: parseInt дава NaN, а || 0 го прави 0). Дванадесет проблема като "undefined is undefined" - безсмислен alert. Модерното правило спира: string-ът не е array.[]: старото правило спира на guard-а (![].length е true); модерното минава нула пъти през loop-а и спира на problems.length === 0 - същият резултат. undefined: и двете спират. Един обект: обектът няма length, затова старото правило спира; Array.isArray е false, затова и модерното спира - и двете пропускат enclosure-а. Поправка: const enclosures = asList(doc.inventory.enclosures); (раздел 10)."4.2.1.7" и в двете: когато няма интервал, split(" ") дава array с целия string, а [0] е този string.Във вашето копие на Homework repository първо вземете новите задачи:
git pull --no-rebase --no-edit upstream main
node 04-arrays-and-records/practice/task1-predict.jsВсеки файл обяснява задачата в началото. Пишете кода си между редовете your code; проверките в края отпечатват OK или FAIL.
| Задача | Файл | Какво правите |
|---|---|---|
| 1 | task1-predict.js | Предсказвате 20 резултата от операции с arrays, после се проверявате |
| 2 | task2-model-list.js | Allow-list от модели: проверка, добавяне без повторения, премахване |
| 3 | task3-split-values.js | split и join: частите на версия като числа, последната част на id, пакетирани списъци и strings key=value |
| 4 | task4-records.js | Списък от 79 записа ключ / стойност: намиране на стойност, броене, събиране |
| 5 | task5-classic-loops.js | Сума, максимум и филтриране върху обикновени arrays и върху таблица |
| Тема | Запомнете |
|---|---|
| Array | Подреден списък; индекс от 0; [...] за създаване; елементи от всеки тип |
| Достъп | arr[i]; липсващ индекс дава undefined, без грешка; последният: arr[arr.length - 1] или arr.at(-1) |
length | Брой елементи, с едно повече от последния индекс; записът в него променя array-я; [] е truthy - проверявайте length === 0 |
| Обект | typeof е "object"; използвайте Array.isArray; === сравнява референции; const не замразява array-я |
| Добавяне / премахване | push / pop в края, unshift / shift в началото, splice навсякъде - те променят array-я |
| Копие | slice(start, end) (end не се включва), [...arr], concat - нови arrays |
| Търсене | indexOf (индекс или -1), includes (true / false), lastIndexOf; точно === - първо нормализирайте |
| Strings | split(sep) -> array от strings; join(sep) -> string; "".split(",") е [""] |
| Loops | for...of за елементите, for с i за индексите и назад; не for...in |
| Модели | Акумулатор (сума, брой), максимум от arr[0], събиране с push, търсене с break |
| Таблици | matrix[row][col]; вложени loops |
| Записи | Array от обекти; намиране, броене, събиране с едно обхождане; без търсене в loop |
| Стар стил | new Array(...), indexOf(x) !== -1, for (var i ...), !arr.length - четете го, пишете модерната форма |
04-arrays-and-records/homework.js (в homework repository): имената в пакетиран string с firmware нива, редове за alert от три списъка, празните pools и една пакетирана настройка в експорта със записи, и още две задачи с loops: четни / нечетни числа и бъг за поправяне в loop-а на една игра.OK.split, от самото правило)? Има ли indexOf(...) !== -1 или for...in върху array, които бихте модернизирали? Променя ли правилото array от doc?Следващото занятие: функции. Helper функциите, които използвахте готови - versionBelow, asList, capitalize - ще напишете сами и ще се научите да четете по-стария код function (x) { ... } в съществуващите правила.