Если вы работали с массивами в JavaScript, то наверняка использовали ряд методов, которые позволяют перебрать все элементы массива и вывести их на экран или подсчитать сумму элементов-чисел. Однако для работы с реальными данными, которые передаются в сети в JSON-файлах или загружаются из базы данных вам, скорей всего, придется работать с массивами объектов.
Вывод массива с объектами
Разница между обычным массивом и массивом объектов не столь уж велика. Просто добавляется, как правило, точечный синтаксис при обращении к свойствам каждого объекта внутри массива. Например, мы можем сформировать нумерованный список при выводе информации о каждом объекте массива:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let animals = [ { name: 'Vasya', type: 'Cat', age: 4}, { name: 'Murka', type: 'Cat', age: 1.5 }, { name: 'Varna', type: 'Turtle', age: 21 }, { name: 'Kesha', type: 'Parrot', age: 3 }, { name: 'Nayda', type: 'Dog', age: 2.5 }, { name: 'Pufic', type: 'Humster', age: 2.5 }, { name: 'Randy', type: 'dog', age: 12 }, ]; document.write('<ol start="0">'); animals.forEach( animal => { document.write(`<li>${animal.type} <span style="color: #1a55cc">${animal.name}</span> is ${animal.age} years old.</li>`); }); document.write('<ol>'); |
Нумерация списка начинается с нуля, для того чтобы сразу был понятен реальный индекс элемента в массиве. Имя животного выводится, как ${animal.name}
, его вид - как ${animal.type}
, а его возраст - как ${animal.age}
. Имя выделено другим цветом.
Сортировка массива с объектами
Еще один момент связан с сортировкой массивов, в которых вместо чисел или строк находятся объекты. У каждого такого объекта может быть одно или несколько строковых свойств и одно или несколько числовых. Для сортировки по каждому из них стоит создавать свою функцию, т.к. передаваемые параметры могут быть разными. Например, мы можем написать такую функцию сортировки массива животных по свойству age
:
1 2 3 |
let sortByAge = (a, b) => a.age > b.age ? 1 : -1; animals.sort(sortByAge); console.log(animals); |
Результат в консоли браузера нам выдаст список, в котором все животные разместились по возрастанию количества прожитых лет. Учтите, что метод array.sort()
преобразует массив. Теперь элементы имеют другие индексы, чем при объявлении массива.
Рассмотрим еще сортировку по именам. Тут принцип точно такой же, но еще мы добавим функцию toLowerCase()
, чтобы имена в верхнем и нижнем регистре отсортировывались по алфавитному порядку:
1 2 3 4 5 |
function sortByName(a, b) { return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1; } animals.sort(sortByName); console.log(animals); |
Результат выполнения сортировки в консоли браузера:
Запись свойств (полей) объекта
Объекты в массивах могут содержать самые разные поля и значения, которые зависят от того, что вам необходимо реализовать в скрипте. Причем названия полей (свойств) объекта могут быть в виде одного или нескольких слов. В последнем случае в обязательном порядке нужно помещать название поля в кавычки и обращаться к такому полу нужно, как к элементу ассоциативного массива. Например, мы имеем такой объект:
1 2 3 4 5 6 7 |
let myObj = { 'model phone': 'Xiaomi Redmi Note 8 Pro 6/128GB', 'processor': 'MediaTek Helio G90T', 'camera': '64 Мп + 8 Мп + 2 Мп + 2 Мп' } console.log(myObj['model phone']) console.log(myObj['processor']); |
Поле 'model phone'
выдаст ошибку, если не взять его в кавычки. Если же оно будет в кавычках, то все будет в порядке.
На данный момент вы даже можете сформировать объект со свойствами (полями), которые имеют кириллические символы. Например, вывод информации о модели телефона можно представить в таком виде:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
let phone1 = { Модель: 'Мобильный телефон Samsung Galaxy M21 4/64GB', Экран: '6.4", Super AMOLED, 2340х1080', Процессор: 'Samsung Exynos 9611 (4 x 2.3 ГГц + 4 x 1.7 ГГц)', 'Количество СИМ-карт': '2 (Nano-SIM)', ОС: 'Android 10 (Q)', Камеры: 'тройная основная камера: 48 Мп + 8 Мп + 5 Мп, фронтальная 20 Мп', 'Оперативная память': 'RAM 4 ГБ', 'Встроенная память': '64 ГБ', 'Дополнительная память': 'microSD (до 512 ГБ)', 'Беспроводные технологии': '3G / LTE / Bluetooth 5.0/ NFC / Wi-Fi', 'Емкость батареи': '6000 мА*ч', Цена: '6299 грн.', 'Цена со скидкой': '5555 грн.' } for(key in phone1){ document.write(`<p><strong>${key}</strong>: ${phone1[key]}</p>`); } |
Такая запись выглядит довольно непривычно, но обрабатывается интерпретатором корректно. В кавычки помещены те свойства объекта, которые содержат в своем названии 2 слова.
Результат выполнения скрипта:
Вывод массива объектов с использованием цикла for ... in
Предположим, что нам нужно вывести ряд html-элементов, например, абзацев, с различным css-форматированием, причем набор правил и текст мы получаем в виде массива объектов. Чаще всего каждый объект в таком массиве имеет набор полей, которые идут в строго определенном порядке, но, предположим, наш массив формировал неопытный студент вручную, поэтому в объектах поля следуют по-разному.
Если посмотреть на названия свойств (полей), то можно заметить, что только свойство text не относится к css-свойствам. Все же остальные и по названию свойства, и по значению относятся к правилам css. Поэтому проще всего собрать css-свойства в одну строку, которая в приведенном ниже скрипте является переменной styleStr
, а затем добавить ее в качестве значения атрибута style для абзаца. С этим нам поможет справится замечательный цикл for...in. Есть только одно но - нам нельзя в css-свойства записать свойство text объекта, поэтому напишем для этой ситуации проверку с помощью if
и пропустим добавление текста к styleStr
с помощью оператора циклов continue
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
const data = [ { color: 'green', 'font-weight': 'bold', padding: '10px', border: '4px dashed #50e690', text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet, libero. Dolor porro, ipsum reprehenderit. ' }, { color: '#1c3dc7', text: 'Ducimus temporibus laboriosam tempora dolorem laborum eligendi cumque adipisci in, vel, quaerat repellat necessitatibus explicabo.', 'font-weight': 'normal', padding: '15px', border: '3px double #57abff', }, ]; data.forEach(item =>{ let styleStr = ''; for(key in item){ if(key == 'text') continue; styleStr+= key+':'+item[key]+';'; } //document.write(`<p style="color: ${item.color};font-weight: ${item['font-weight']}; padding: ${item['padding']}; border: ${item.border} ">${item.text}</p>`); document.write(`<p style="${styleStr}">${item.text}</p>`); }) |
Обратите внимание, насколько длиннее строка 23, в которой мы записали все свойства без использования цикла и подумайте, насколько больше шансов допустить в ней ошибку по сравнению с использованием цикла for...in
.
Результат работы скрипта:
Использование функций для вывода массива объектов
Давайте теперь несколько усложним себе задачу. Представим, что у нас есть кафе, в котором в виде объектов реализованы товары-блюда, которые можно выбирать, указывая их индекс и количество.
Для начала нам нужно сформировать сам массив объектов-блюд, каждый из которых внутри себя содержит такие свойства: название блюда, его фото, вес в граммах, цену в гривнах и количество в штуках (по умолчанию 1). Например, это выглядит таким образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
const food = [{ name: "Паста «Болоньезе»", img: "food/pasta-bolonez.jpg", weight: 350, price: 68, quantity: 1 }, { name: "Спагетти с овощами", img: "food/pasta-s-ovoshhami.jpg", weight: 350, price: 56, quantity: 1 }, { name: "Пене с куриным филе", img: "food/pene.jpg", weight: 400, price: 68, quantity: 1 }, { name: "Пицца «Куриная с ананасами»", img: "food/pizza-kuricza-ananas.jpg", weight: 675, price: 139, quantity: 1 }, { name: "Пицца «Четыре сезона метровая»", img: "food/pizza-4_sezona.jpg", weight: 1600, price: 339, quantity: 1 }, { name: "Пицца «Итальяни»", img: "food/pizza-italyani.jpg", weight: 740, price: 159, quantity: 1 }, { name: "Салат «Джонатан с семгой»", img: "food/salat-dzhonotan-s-semgoj.jpg", weight: 230, price: 77, quantity: 1 }, { name: "Салат «Цезарь с креветкой»", img: "food/salat-czezar-s-krevetkoj.jpg", weight: 230, price: 69, quantity: 1 }]; |
Для оформления html-страницы нам понадобится ссылка на Bootstrap-4 с CDN и некоторые стили. Разметка будет простой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Заказ еды</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"> <style> .card-title h3 { font-size: 1.2em; font-weight: bold; min-height: 55px; margin-bottom: 0; } .card-body { padding-top: .5rem; position: relative; } .quantity { position: absolute; bottom: 10px; right: 10px; padding: 9px; } </style> </head> <body> <div class="container text-center"> <h1 class="text-success">Заказ еды на дом</h1> </div> <script src="js/food-order.js"></script> </body> </html> |
Весь скрипт мы поместим в файл food-order.js
. Вы можете назвать его по-другому, если захотите. Все основные html-теги мы будем выводить методом document.write()
. Возможно, это не самый лучший способ, но он нагляден и дает понять, как работают функции.
При выводе блюд мы воспользуемся компонентом Bootstrap-4 Карточка с различными цветовыми классами для выделения тех или иных блюд. Кроме того, вам понадобится понимание того, как работает тернарный оператор в JavaScript.
Функция для вывода массива объектов
Выведем массив с помощью функции printProduct()
. В ней мы переберем все элементы массива методом forEach()
и используем точечный снтаксис для обращения к нужным в разметке свойствам объекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function printProduct() { document.write(`<div class="container text-center"> <h2 class="text-success my-4">Вывод продуктов на экран</h2> <div class="row">`); food.forEach((product, ind) => { document.write(`<div class="col-lg-3 col-md-4 col-sm-6 mb-3"> <div class="card border-success"> <img src="${product.img}" alt="${product.name}" class="img-fluid"> <div class="card-title pt-2 bg-success text-white"> <h3 class="text-=success"><span class="badge badge-light">${ind}</span> ${product.name}</h3> </div> <div class="card-body"> <div class="card-text weight">${product.weight} г</div> <div class="card-text price">${product.price} грн.</div> </div> </div> </div>`); }); document.write(`</div></div>`); } printProduct(); |
Смотрим на полученный результат (открыть в новой вкладке):
Вывод количества продуктов
Следующая задача - изменить количество блюд так, как если бы их заказали по телефону, например. Мы сделаем 2 функции: первая будет изменять свойство quantity
в объекте по переданному индексу, а вторая - отвечать за вывод блюд с указанием их количества. Если свойство quantity
будет больше 1, то при выводе мы будем менять классы для управления цветом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
function addQuantity(ind,qn) { food[ind].quantity = qn; } function printProductQn() { document.write(`<div class="container text-center"> <h2 class="text-primary my-4">Вывод продуктов с указанием их количества</h2> <div class="row">`); food.forEach((product, ind) => { let qn = product.quantity; document.write(`<div class="col-lg-3 col-md-4 col-sm-6 mb-4"> <div class="card ${qn >1 ? "border-primary": "border-success"}"> <img src="${product.img}" alt="${product.name}" class="img-fluid"> <div class="card-title pt-2 text-white ${qn >1 ? "bg-primary":"bg-success"}"> <h3 class="text-=success"><span class="badge badge-light">${ind}</span> ${product.name}</h3> </div> <div class="card-body"> <div class="card-text weight">${product.weight} г</div> <div class="card-text price">${product.price} грн.</div> <div class="badge quantity ${qn >1 ? "badge-primary": "badge-success"}">${qn} шт.</div> </div> </div> </div>`); }); document.write(`</div></div>`); } addQuantity(0, 5); addQuantity(3, 10); addQuantity(4, 3); printProductQn(); |
Для того чтобы отделить блюда с количеством большим, чем 1, выделим их голубым цветом, применив класс bg-primary
, badge-primary
, border-primary
Bootstrap-4.
Обратите внимание на строку
1 |
`<div class="card ${qn >1 ? "border-primary": "border-success"}">` |
В данном случае мы используем шаблонные строки из стандарта ES6 и тернарный оператор, но не для вывода конкретного значения переменной, а для получения этого значения в зависимости от проверяемого условия. Читается это так: если количество (переменная qn
) больше 1, выводим класс "border-primary"
, иначе выводим класс "border-success"
. Сделано это для того, чтобы не вводить дополнительных переменных для проверки этого и подобных условий.
Посмотреть пример в новой вкладке.
Выбор одного продукта
Теперь создадим функцию, которая выведет один выбранный по указанному индексу продукт.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function chooseProduct(productId) { document.write(`<div class="container text-center"> <h2>Заказ одного продукта</h2> <div class="row">`); food.forEach((product, ind) => { document.write(`<div class="col-lg-3 col-md-4 col-sm-6 mb-4"> <div class="card ${ind==productId ? "border-warning shadow" :"border-success"}"> <img src="${product.img}" alt="${product.name}" class="img-fluid"> <div class="card-title pt-2 ${ind==productId ? "bg-warning":"border-bottom border-top border-success text-success" }"> <h3 class="text-success"><span class="${ind==productId ? "badge badge-light":"badge badge-success"}"> ${ind}</span> ${product.name}</h3> </div> <div class="card-body"> <div class="card-text weight">${product.weight} г.</div> <div class="card-text price">${product.price} грн.</div> </div> </div> </div>`); }); document.write(`</div></div>`); } chooseProduct(2); |
В этой функции мы проверяем, совпадает ли индекс элемента массива с параметром, переданныи в функцию, и меняем внешний вид блока для этого элемента массива. Для проверки используется тернарный оператор, аналогично тому, как мы делали это в предыдущей функции. Меняются только условие и названия классов.
Вы можете открыть пример в новой вкладке.
Выбор нескольких продуктов
Недостатком предыдущей функции является то, что при повторном ее вызове будет выведен еще один блок со всеми блюдами, в котором выделено лишь одно. Хотелось бы получить сразу несколько выделенных блюд, если в функцию мы передаем несколько параметров, а именно индексов элементов массива food
. Давайте запишем такую функцию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function chooseProducts(...productId) { //console.log(productId); document.write(`<div class="container text-center"> <h2>Заказ нескольких продуктов</h2> <div class="row">`); food.forEach((product, ind) => { document.write(`<div class="col-lg-3 col-md-4 col-sm-6 mb-4"> <div class="card ${ productId.includes(ind) ? "border-warning shadow" :"border-success"}"> <img src="${product.img}" alt="${product.name}" class="img-fluid"> <div class="card-title pt-2 ${productId.includes(ind) ? "bg-warning": "border-bottom border-top border-success text-success" } }"> <h3 class="text-=success"><span class="${productId.includes(ind) ? "badge badge-light":"badge badge-success"}">${ind}</span> ${product.name}</h3> </div> <div class="card-body"> <div class="card-text weight">${product.weight} г</div> <div class="card-text price">${product.price} грн.</div> </div> </div> </div>`); }); document.write(`</div></div>`); } chooseProducts(0, 2, 4, 6); |
Здесь вроде бы не так много изменений по сравнению с предыдущей функцией. Даже не переименован аргумент функции, зато перед ним добавились три точки... Это оператор rest, добавленный в стандарте ES6, который позволяет преобразовать переданные параметры в массив и использовать для этого массива все методы класса Array, встроенного в JavaScript.
В нашей функции мы проверяем, содержит ли массив переданных в функцию индексов тот, который мы задаем перебирающим методом forEach(). Для этого нм нужен метод includes()
.В том случае, если совпадение индексов есть, меняем классы для отображения блока.
Результат работы функции (открыть в новой вкладке):
Выбор нескольких продуктов с указанием их количества
В последней функции хочется объединить выбор нескольких продуктов с указанием их количества. Вот только возникает вопрос - каким образом нам передавать параметры в эту функцию? До сих пор мы ограничивались либо одним параметром, либо несколькими, которые затем с помощью rest-оператора преобразовывали в массив. Почему бы не воспользоваться подобным подходом и здесь, но немного с другой стороны? Т.е. мы будем передавать несколько параметров, которые будут сами небольшими массивами из 2-х элементов, первый из которых отвечает за индекс объекта-блюда в массиве food
, а второй - за количество таких блюд. Не очень понятно? Тогда смотрим на код функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
function chooseProductsQn(...prodData) { console.log(prodData); document.write(`<div class="container text-center"> <h2 class="text-secondary my-4">Заказ нескольких продуктов с указанием их количества</h2> <div class="row">`); food.forEach((product, ind) => { let order = false, qn = 0; prodData.forEach(item => { // console.log(item[0],item[1]); if (item[0]==ind) { order = true; qn = item[1]; } }); document.write(`<div class="col-lg-3 col-md-4 col-sm-6 mb-4"> <div class="card ${ order ? "border-danger shadow" :"border-success"}"> <img src="${product.img}" alt="${product.name}" class="img-fluid"> <div class="card-title pt-2 ${order? "bg-danger text-white": "border-bottom border-top border-success text-success" } }"> <h3><span class="${order ? "badge badge-light":"badge badge-success"}"> ${ind}</span> ${product.name}</h3> </div> <div class="card-body"> <div class="card-text weight">${product.weight} г</div> <div class="card-text price">${product.price} грн.</div> ${qn >0 ?'<div class="badge badge-danger quantity">'+qn+' шт.</div>' :''} </div> </div> </div>`); }); document.write(`</div></div>`); } chooseProductsQn([1, 6], [2, 4], [3, 6], [6,2]); |
Во второй строке мы выводим в консоль браузера передаваемые с помощью оператора rest параметы и видим, что у нас есть 4 массива внутри одного общего.
В функции chooseProductsQn()
в строке 11 мы проверяем, если индекс передаваемого параметра (item[0]
) и индекс продукта (ind
) совпадают, то мы меняем классы для вывода html-разметки блока.
Давайте посмотрим, как функция отформатирует нам вывод блоков (открыть в новой вкладке).