Object.keys, values, entries
Давайте отойдём от отдельных структур данных и поговорим об их переборе вообще.
В предыдущей главе мы видели методы map.keys() , map.values() , map.entries() .
Это универсальные методы, и существует общее соглашение использовать их для структур данных. Если бы мы делали собственную структуру данных, нам также следовало бы их реализовать.
Методы поддерживаются для структур:
Простые объекты также можно перебирать похожими методами, но синтаксис немного отличается.
Object.keys, values, entries
Для простых объектов доступны следующие методы:
- Object.keys(obj) – возвращает массив ключей.
- Object.values(obj) – возвращает массив значений.
- Object.entries(obj) – возвращает массив пар [ключ, значение] .
Обратите внимание на различия (по сравнению с map , например):
| Map | Object | |
|---|---|---|
| Синтаксис вызова | map.keys() | Object.keys(obj) , не obj.keys() |
| Возвращает | перебираемый объект | «реальный» массив |
Первое отличие в том, что мы должны вызвать Object.keys(obj) , а не obj.keys() .
Почему так? Основная причина – гибкость. Помните, что объекты являются основой всех сложных структур в JavaScript. У нас может быть объект data , который реализует свой собственный метод data.values() . И мы всё ещё можем применять к нему стандартный метод Object.values(data) .
Второе отличие в том, что методы вида Object.* возвращают «реальные» массивы, а не просто итерируемые объекты. Это в основном по историческим причинам.
let user = < name: "John", age: 30 >;
- Object.keys(user) = [«name», «age»]
- Object.values(user) = [«John», 30]
- Object.entries(user) = [ [«name»,»John»], [«age»,30] ]
Вот пример использования Object.values для перебора значений свойств в цикле:
let user = < name: "John", age: 30 >; // перебор значений for (let value of Object.values(user)) < alert(value); // John, затем 30 >
Object.keys/values/entries игнорируют символьные свойства
Так же, как и цикл for..in , эти методы игнорируют свойства, использующие Symbol(. ) в качестве ключей.
Обычно это удобно. Но если требуется учитывать и символьные ключи, то для этого существует отдельный метод Object.getOwnPropertySymbols, возвращающий массив только символьных ключей. Также, существует метод Reflect.ownKeys(obj), который возвращает все ключи.
Трансформации объекта
У объектов нет множества методов, которые есть в массивах, например map , filter и других.
Если мы хотели бы их применить, то можно использовать Object.entries с последующим вызовом Object.fromEntries :
- Вызов Object.entries(obj) возвращает массив пар ключ/значение для obj .
- На нём вызываем методы массива, например, map .
- Используем Object.fromEntries(array) на результате, чтобы преобразовать его обратно в объект.
Например, у нас есть объект с ценами, и мы хотели бы их удвоить:
let prices = < banana: 1, orange: 2, meat: 4, >; let doublePrices = Object.fromEntries( // преобразовать в массив, затем map, затем fromEntries обратно объект Object.entries(prices).map(([key, value]) => [key, value * 2]) ); alert(doublePrices.meat); // 8
Это может выглядеть сложным на первый взгляд, но становится лёгким для понимания после нескольких раз использования.
Можно делать и более сложные «однострочные» преобразования таким путём. Важно только сохранять баланс, чтобы код при этом был достаточно простым для понимания.
Задачи
Сумма свойств объекта
важность: 5
Есть объект salaries с произвольным количеством свойств, содержащих заработные платы.
Напишите функцию sumSalaries(salaries) , которая возвращает сумму всех зарплат с помощью метода Object.values и цикла for..of .
Если объект salaries пуст, то результат должен быть 0 .
let salaries = < "John": 100, "Pete": 300, "Mary": 250 >; alert( sumSalaries(salaries) ); // 650
function sumSalaries(salaries) < let sum = 0; for (let salary of Object.values(salaries)) < sum += salary; >return sum; // 650 > let salaries = < "John": 100, "Pete": 300, "Mary": 250 >; alert( sumSalaries(salaries) ); // 650
Или, как вариант, мы можем получить сумму, используя методы Object.values и reduce :
// reduce перебирает массив значений salaries, // складывает их // и возвращает результат function sumSalaries(salaries) < return Object.values(salaries).reduce((a, b) =>a + b, 0) // 650 >
Ассоциативные массивы в JavaScript
В этой статье мы поговорим про ассоциативные массивы. Поймём, что это такое, рассмотрим основные принципы работы с такими массивами. А также обсудим, как создать ассоциативный массив в JavaScript.
Ассоциативный массив — что это?
Под ассоциативным массивом подразумевают массив, в котором в качестве ключей применяются строки. То есть речь идёт о совокупности пар «ключ-значение». Таким образом, в ассоциативном массиве любое значение связано с конкретным ключом, а доступ к этому значению производится по имени ключа.
Мы можем представить ассоциативный массив в виде небольшого ящика, где находятся отделения. Каждое отделение имеет имя (это ключ) и содержимое (это значение). Естественно, чтобы найти нужное отделение в ящике, мы должны знать имя отделения (ключ). Зная это имя, мы сможем получить содержимое отделения (значение).
Создаём ассоциативный массив в JavaScript
Начиная с релиза ECMAScript 2015 (6), в JavaScript мы можем использовать для создания ассоциативного массива объект Map. До этого релиза в JavaScript не существовали типы данных, предназначенные лишь для создания ассоциативных массивов, поэтому создавались такие массивы с помощью объектов.
Как вы должны помнить, в JavaScript есть тип данных Array. Но этот тип данных служит лишь для создания массивов, в которых в роли ключей применяются числа (индексы).
Ассоциативный массив: объект Map
Как мы уже сказали, для создания ассоциативных массивов в JavaScript используется тип данных Map. При этом в виде ключа мы можем использовать и примитивные, и ссылочные типы данных.
Давайте посмотрим, как создаётся ассоциативный массив (экземпляр объекта Map):
// создаём пустой ассоциативный массив var arr1 = new Map(); // создаём ассоциативный массив и добавляем три пары "ключ-значение" var arr = new Map([ ['key1', 'value1'], ['key2', 'value2'], ['key3', 'value3'] ]);Если мы хотим узнать число элементов в массиве, пригодится свойство size:
arr.size; // 3Чтобы добавить элемент в массив (то есть в экземпляр объекта Map), используем метод set:
// добавляем в массив пару "ключ-значение" arr.set('key4','value4'); // добавляем в массив несколько пар "ключ-значение" arr.set('key5','value5'); arr.set('key6','value6'); // или так arr .set('key5','value5') .set('key6','value6');Когда в массиве такой ключ уже есть, установится новое значение, связанное с ним:
arr.set('key1','new value');Чтобы получить значение по ключу, используем метод get:
// получаем значение, которое ассоциировано с ключом 'key4' arr.get('key4'); // 'value4'Если хотим проверить, есть ли ключ в массиве, нам пригодится метод has:
// существует ли в массиве arr ключ key2 arr.has('key2'); // trueЧтобы удалить из ассоциативного JavaScript-массива элемент по имени ключа, применяем метод delete:
arr.delete('key1'); // trueЭтот метод вернёт true, если ключ существовал в массиве, иначе он вернёт false.
if (arr.delete('key1')) < console.log('Запись с ключом "key1" удалена из массива!'); >else
Чтобы очистить массив и удалить все элементы, подойдёт метод clear.
arr.clear(); // очищаем массив arr arr.size; // 0 (число элементов)Перебор ассоциативного массива в JavaScript
Как правило, перебор ассоциативного массива осуществляется посредством цикла for. of. Итерацию мы можем организовать по ключам, записям и значениям ([key, values]).
Мы можем выполнить перебор ключей с помощью итерируемого объекта MapIterator, который возвращается посредством метода keys:
for (let key of arr.keys())
Чтобы перебрать значения, воспользуйтесь итерируемым объектом MapIterator, который возвращается посредством методом values:
for (let value of arr.values())
Теперь посмотрим, как происходит перебор записей ассоциативного массива в JavaScript с применением метода entries:
for (let pair of arr.entries()) < // pair - это массив [key, values] console.log(pair[0]); // ключ console.log(pair[1]); // значение console.log(`Ключ = $, значение = $`); >Этот метод по умолчанию применяется в for. of, поэтому его можно опустить:
for (let pair of arr) < console.log(`Ключ = $, значение = $`); >Вдобавок ко всему, перебрать ассоциативный массив в JavaScript мы можем посредством метода forEach.
arr.forEach(function(value,key) < console.log('key = ' + key +', value = ' + value); >);Если нужно преобразовать ассоциативный массив в JSON и назад, подойдёт следующий способ:
let arr = new Map([ ['question', 'Текст вопроса. '], ['answer1', 'Ответ 1. '], ['answer2', 'Ответ 2. '], ]); // в JSON jsonStr = JSON.stringify([. arr]); // из JSON в Map mapArr = new Map(JSON.parse(jsonStr));Ассоциативный JavaScript-массив как объект
В виде ассоциативного массива мы можем использовать и объект.
Для создания пустого ассоциативного массива (объекта) нам подойдёт один из следующих вариантов:
// с помощью литерала объекта var arr = <>; // с помощью стандартной функции-конструктора Object var arr = new Object(); // с помощью Object.create var arr = new Object.create(null);Чтобы заполнить ассоциативный массив в момент его создания, поступаем следующим образом:
var myArray =
Теперь добавим в наш ассоциативный массив элемент (пару «ключ-значение»):
// добавляем в массив arr строку «текстовое значение», которое связано с ключом «key1» arr["key1"] = "текстовое значение" // добавляем в массив число 22, которое связано с ключом «key2» arr["key2"] = 22;Обратите внимание, что добавление элемента в JavaScript-массив выполнится лишь тогда, когда данного ключа в нём нет. Если ключ уже имеется, то выражение лишь поменяет значение уже существующего ключа.
В роли значения ключа мы можем использовать любой тип данных, включая объекты. Стоит добавить, что в JavaScript кроме записи с квадратными скобками мы можем использовать точку. Однако это доступно лишь для ключей, имена которых соответствуют правилам именования переменных.
arr.key1 = "текстовое значение" arr.key2 = 22;Чтобы получить значение элемента по ключу, подойдёт следующий синтаксис:
myArray["key1"]; myArray["key2"]; myArray.key1; myArray.key2;Чтобы получить число ключей (длину) ассоциативного массива, поступаем следующим образом:
var myArray = < "key1":"value1", "key2":"value2", "key3":"value3">// 1 – получаем массив ключей посредством метода keys // 2 - применяем свойство length, дабы узнать длину массива Object.keys(myArray).length; // 3Если надо удалить элемент из ассоциативного массива, применяем оператор delete.
delete myArray["key1"];Когда нужно проверить, существует ли ключ в нашем ассоциативном массиве:
var myArray = ; // 1 способ (задействуем метод hasOwnProperty) if (myArray.hasOwnProperty("key1")) < console.log("Ключ key1 есть!"); >else < console.log("Ключ key1 не существует!"); >// 2 способ if ("key1" in myArray) < console.log("Ключ key1 существует в массиве!"); >else
Если нужно перебрать элементы ассоциативного массива, подойдёт цикл for. in:
// myArray — ассоциативный массив for(key in myArray)
А чтобы преобразовать ассоциативный JavaScript-массив в JSON и назад, поступаем так:
// Ассоциативный массив (объект) var myArr = < key1: "value1", key2: "value2", key3: "value3" >; // в JSON jsonStr = JSON.stringify(myArr); // из JSON в ассоциативный массив arr = JSON.parse(jsonStr); //получаем значение по ключу key1 (выводим в консоль) console.log(arr.key1);Как создать ассоциативный массив js
Под ассоциативным массивом подразумевают массив, в котором в качестве ключей применяются строки. То есть речь идёт о совокупности пар «ключ-значение». Таким образом, в ассоциативном массиве любое значение связано с конкретным ключом, а доступ к этому значению производится по имени ключа.
Начиная с релиза ECMAScript 2015 (6), в JavaScript мы можем использовать для создания ассоциативного массива объект Map.//создаём пустой ассоциативный массив const associativeArray = new Map(); //либо создаём ассоциативный массив сразу с тремя парами 'ключ-значений'. const associativeArray = new Map([ ['key1', 'value1'], ['key2', 'value2'], ['key3', 'value3'] ]); //узнаём число элементов console.log(associativeArray.size); // => 3 //добавляем пару 'ключ-значение' associativeArray.set('key5','value5'); //получаем значение по ключу associativeArray.get('key3'); // => 'value3' //проверяем, содержит ли массив ключ 'key2' associativeArray.has('key2'); // => true //удаляем пару 'ключ-значение' по ключу //если элемент существовал, то вернётся true, в противном случае - false associativeArray.delete('key1'); // => true //очищаем массив associativeArray.clear();05 апреля 2023
Для создания ассоциативного массива в JavaScript можно использовать объекты. Объекты в JavaScript могут содержать ключи и значения, где ключи представляют собой строки или символы, а значения могут быть любого типа данных, включая другие объекты.
Пример создания ассоциативного массива в JavaScript:
const myObj = name: 'John', age: 30, hobbies: ['reading', 'traveling', 'cooking'], address: street: '123 Main St', city: 'New York', state: 'NY', >, >;В этом примере мы создали объект myObj , который содержит ключи name , age , hobbies и address . Ключи name и age содержат значения типа string и number соответственно, а ключ hobbies содержит массив строк. Ключ address содержит вложенный объект с ключами street , city и state .
Объекты
Как мы знаем из главы Типы данных, в JavaScript существует 8 типов данных. Семь из них называются «примитивными», так как содержат только одно значение (будь то строка, число или что-то другое).
Объекты же используются для хранения коллекций различных значений и более сложных сущностей. В JavaScript объекты используются очень часто, это одна из основ языка. Поэтому мы должны понять их, прежде чем углубляться куда-либо ещё.
Объект может быть создан с помощью фигурных скобок с необязательным списком свойств. Свойство – это пара «ключ: значение», где ключ – это строка (также называемая «именем свойства»), а значение может быть чем угодно.
Мы можем представить объект в виде ящика с подписанными папками. Каждый элемент данных хранится в своей папке, на которой написан ключ. По ключу папку легко найти, удалить или добавить в неё что-либо.
Пустой объект («пустой ящик») можно создать, используя один из двух вариантов синтаксиса:
let user = new Object(); // синтаксис "конструктор объекта" let user = <>; // синтаксис "литерал объекта"Обычно используют вариант с фигурными скобками <. >. Такое объявление называют литералом объекта или литеральной нотацией.
Литералы и свойства
При использовании литерального синтаксиса <. >мы сразу можем поместить в объект несколько свойств в виде пар «ключ: значение»:
let user = < // объект name: "John", // под ключом "name" хранится значение "John" age: 30 // под ключом "age" хранится значение 30 >;У каждого свойства есть ключ (также называемый «имя» или «идентификатор»). После имени свойства следует двоеточие ":" , и затем указывается значение свойства. Если в объекте несколько свойств, то они перечисляются через запятую.
В объекте user сейчас находятся два свойства:
- Первое свойство с именем "name" и значением "John" .
- Второе свойство с именем "age" и значением 30 .
Можно сказать, что наш объект user – это ящик с двумя папками, подписанными «name» и «age».
Мы можем в любой момент добавить в него новые папки, удалить папки или прочитать содержимое любой папки.
Для обращения к свойствам используется запись «через точку»:
// получаем свойства объекта: alert( user.name ); // John alert( user.age ); // 30Значение может быть любого типа. Давайте добавим свойство с логическим значением:
user.isAdmin = true;Для удаления свойства мы можем использовать оператор delete :
delete user.age;Имя свойства может состоять из нескольких слов, но тогда оно должно быть заключено в кавычки:
let user = < name: "John", age: 30, "likes birds": true // имя свойства из нескольких слов должно быть в кавычках >;Последнее свойство объекта может заканчиваться запятой:
let user =Это называется «висячая запятая». Такой подход упрощает добавление, удаление и перемещение свойств, так как все строки объекта становятся одинаковыми.
Объект, объявленный как константа, может быть изменён
Объект, объявленный через const , может быть изменён.
const user = < name: "John" >; user.name = "Pete"; // (*) alert(user.name); // PeteМожет показаться, что строка (*) должна вызвать ошибку, но нет, здесь всё в порядке. Дело в том, что объявление const защищает от изменений только саму переменную user , а не её содержимое.
Определение const выдаст ошибку только если мы присвоим переменной другое значение: user=. .
Есть ещё один способ сделать константами свойства объекта, который мы рассмотрим в главе Флаги и дескрипторы свойств.
Квадратные скобки
Для свойств, имена которых состоят из нескольких слов, доступ к значению «через точку» не работает:
// это вызовет синтаксическую ошибку user.likes birds = trueJavaScript видит, что мы обращаемся к свойству user.likes , а затем идёт непонятное слово birds . В итоге синтаксическая ошибка.
Точка требует, чтобы ключ был именован по правилам именования переменных. То есть не имел пробелов, не начинался с цифры и не содержал специальные символы, кроме $ и _ .
Для таких случаев существует альтернативный способ доступа к свойствам через квадратные скобки. Такой способ сработает с любым именем свойства:
let user = <>; // присваивание значения свойству user["likes birds"] = true; // получение значения свойства alert(user["likes birds"]); // true // удаление свойства delete user["likes birds"];Сейчас всё в порядке. Обратите внимание, что строка в квадратных скобках заключена в кавычки (подойдёт любой тип кавычек).
Квадратные скобки также позволяют обратиться к свойству, имя которого может быть результатом выражения. Например, имя свойства может храниться в переменной:
let key = "likes birds"; // то же самое, что и user["likes birds"] = true; user[key] = true;Здесь переменная key может быть вычислена во время выполнения кода или зависеть от пользовательского ввода. После этого мы используем её для доступа к свойству. Это даёт нам большую гибкость.
let user = < name: "John", age: 30 >; let key = prompt("Что вы хотите узнать о пользователе?", "name"); // доступ к свойству через переменную alert( user[key] ); // John (если ввели "name")Запись «через точку» такого не позволяет:
let user = < name: "John", age: 30 >; let key = "name"; alert( user.key ); // undefinedВычисляемые свойства
Мы можем использовать квадратные скобки в литеральной нотации для создания вычисляемого свойства.
let fruit = prompt("Какой фрукт купить?", "apple"); let bag = < [fruit]: 5, // имя свойства будет взято из переменной fruit >; alert( bag.apple ); // 5, если fruit="apple"Смысл вычисляемого свойства прост: запись [fruit] означает, что имя свойства необходимо взять из переменной fruit .
И если посетитель введёт слово "apple" , то в объекте bag теперь будет лежать свойство .
По сути, пример выше работает так же, как и следующий пример:
let fruit = prompt("Какой фрукт купить?", "apple"); let bag = <>; // имя свойства будет взято из переменной fruit bag[fruit] = 5;…Но первый пример выглядит лаконичнее.
Мы можем использовать и более сложные выражения в квадратных скобках:
let fruit = 'apple'; let bag = < [fruit + 'Computers']: 5 // bag.appleComputers = 5 >;Квадратные скобки дают намного больше возможностей, чем запись через точку. Они позволяют использовать любые имена свойств и переменные, хотя и требуют более громоздких конструкций кода.
Подведём итог: в большинстве случаев, когда имена свойств известны и просты, используется запись через точку. Если же нам нужно что-то более сложное, то мы используем квадратные скобки.
Свойство из переменной
В реальном коде часто нам необходимо использовать существующие переменные как значения для свойств с тем же именем.
function makeUser(name, age) < return < name: name, age: age // . другие свойства >; > let user = makeUser("John", 30); alert(user.name); // JohnВ примере выше название свойств name и age совпадают с названиями переменных, которые мы подставляем в качестве значений этих свойств. Такой подход настолько распространён, что существуют специальные короткие свойства для упрощения этой записи.
Вместо name:name мы можем написать просто name :
function makeUser(name, age) < return < name, // то же самое, что и name: name age // то же самое, что и age: age // . >; >Мы можем использовать как обычные свойства, так и короткие в одном и том же объекте:
let user = < name, // тоже самое, что и name:name age: 30 >;Ограничения на имена свойств
Как мы уже знаем, имя переменной не может совпадать с зарезервированными словами, такими как «for», «let», «return» и т.д.
Но для свойств объекта такого ограничения нет:
// эти имена свойств допустимы let obj = < for: 1, let: 2, return: 3 >; alert( obj.for + obj.let + obj.return ); // 6Иными словами, нет никаких ограничений к именам свойств. Они могут быть в виде строк или символов (специальный тип для идентификаторов, который будет рассмотрен позже).
Все другие типы данных будут автоматически преобразованы к строке.
Например, если использовать число 0 в качестве ключа, то оно превратится в строку "0" :
let obj = < 0: "Тест" // то же самое что и "0": "Тест" >; // обе функции alert выведут одно и то же свойство (число 0 преобразуется в строку "0") alert( obj["0"] ); // Тест alert( obj[0] ); // Тест (то же свойство)Есть небольшой подводный камень, связанный со специальным свойством __proto__ . Мы не можем установить его в необъектное значение:
let obj = <>; obj.__proto__ = 5; // присвоим число alert(obj.__proto__); // [object Object], значение - это объект, т.е. не то, что мы ожидалиКак мы видим, присвоение примитивного значения 5 игнорируется.
Мы более подробно исследуем особенности свойства __proto__ в следующих главах Прототипное наследование, а также предложим способы исправления такого поведения.
Проверка существования свойства, оператор «in»
В отличие от многих других языков, особенность JavaScript-объектов в том, что можно получить доступ к любому свойству. Даже если свойства не существует – ошибки не будет!
При обращении к свойству, которого нет, возвращается undefined . Это позволяет просто проверить существование свойства:
let user = <>; alert( user.noSuchProperty === undefined ); // true означает "свойства нет"Также существует специальный оператор "in" для проверки существования свойства в объекте.
"key" in object