Map и Set в JavaScript #
Map и Set — встроенные коллекции ES2015: Map хранит пары ключ–значение с ключами любого типа, Set — только уникальные значения. Они дополняют обычные объекты {} и массивы, когда важны частые добавления/удаления, предсказуемый размер или гарантия уникальности (теги трека, id курьеров, кэш ETA).
Set: уникальные значения #
const tags = new Set(['focus', 'work', 'focus', 'instrumental'])
tags.size // 3
tags.has('work') // true
tags.add('night')
tags.delete('work')
Порядок итерации — порядок вставки.
Преобразование массива в уникальный список:
const rawTags = ['pop', 'work', 'pop', 'jazz', 'work']
const unique = [...new Set(rawTags)]
// ["pop", "work", "jazz"]
Проверка дубликата sku в корзине без объекта-словаря:
function hasDuplicateSkus(lines) {
const seen = new Set()
for (const line of lines) {
if (seen.has(line.sku)) return true
seen.add(line.sku)
}
return false
}
Перебор Set #
const zones = new Set(['north', 'center', 'south'])
for (const zone of zones) {
console.log(zone)
}
zones.forEach((zone) => console.log(zone))
const list = [...zones] // массив из Set
Map: словарь с любыми ключами #
const etaByZone = new Map()
etaByZone.set('center', 25)
etaByZone.set('suburb', 45)
etaByZone.get('center') // 25
etaByZone.has('north') // false
etaByZone.delete('suburb')
etaByZone.size // 1
Ключом может быть объект (по ссылке), число, символ:
const feeByTier = new Map([
['standard', 199],
['express', 399],
])
feeByTier.get('express') // 399
Инициализация из массива пар — удобно для статических таблиц тарифов.
Map vs обычный Object #
| Критерий | Map | {} |
|---|---|---|
| Ключи | любые значения | строки и символы |
| Порядок | порядок вставки | частично гарантирован для строковых ключей |
| Размер | map.size | вручную Object.keys(obj).length |
| Частые add/delete | оптимизирован | менее предсказуемо |
| JSON | не сериализуется напрямую | да |
Для JSON API по-прежнему объекты; Map — для runtime-кэша в памяти (например, расчёт доставки по зонам в сессии чекаута).
Итерация Map #
const trackById = new Map([
['t1', { title: 'Intro' }],
['t2', { title: 'Main' }],
])
for (const [id, track] of trackById) {
console.log(id, track.title)
}
for (const id of trackById.keys()) {
console.log(id)
}
for (const track of trackById.values()) {
console.log(track.title)
}
const entries = [...trackById.entries()]
Преобразования Map ↔ Object ↔ Array #
Object → Map:
const tariffs = { standard: 199, express: 399 }
const tariffMap = new Map(Object.entries(tariffs))
Map → Object (осторожно с нестроковыми ключами):
const plain = Object.fromEntries(tariffMap)
// { standard: 199, express: 399 }
Set из массива и обратно — см. выше через spread.
WeakMap и WeakSet (кратко) #
WeakMap и WeakSet хранят слабые ссылки на объекты-ключи: если на объект больше нет ссылок, запись может быть собрана сборщиком мусора. Нет итерации и size.
Типичное применение — приватные метаданные DOM-элементов или кэш, привязанный к жизненному циклу объекта. В учебных примерах корзины чаще достаточно обычного Map.
Практика: кэш расчёта доставки #
const deliveryCache = new Map()
function etaMinutes(zone, loadFactor) {
const key = `${zone}:${loadFactor}`
if (deliveryCache.has(key)) {
return deliveryCache.get(key)
}
const base = zone === 'center' ? 20 : 40
const minutes = Math.round(base * loadFactor)
deliveryCache.set(key, minutes)
return minutes
}
etaMinutes('center', 1.2) // считает и кэширует
etaMinutes('center', 1.2) // из кэша
Практика: уникальные исполнители в плейлисте #
const playlist = [
{ title: 'A', artist: 'Lumen' },
{ title: 'B', artist: 'Echo' },
{ title: 'C', artist: 'Lumen' },
]
const artists = [...new Set(playlist.map((t) => t.artist))]
// ["Lumen", "Echo"]
function countByCategory(lines) {
const counts = new Map()
for (const line of lines) {
const prev = counts.get(line.category) ?? 0
counts.set(line.category, prev + 1)
}
return counts
}
countByCategory([
{ category: 'gear', sku: 'a' },
{ category: 'digital', sku: 'b' },
{ category: 'gear', sku: 'c' },
])
// Map { 'gear' => 2, 'digital' => 1 }
Практика: быстрый поиск по id #
function indexById(items) {
return new Map(items.map((item) => [item.id, item]))
}
const scoreIndex = indexById([
{ id: 'u1', score: 100 },
{ id: 'u2', score: 250 },
])
scoreIndex.get('u2') // { id: 'u2', score: 250 }
Для небольших списков достаточно find; Map выигрывает при частых lookup и большом числе записей.
Итог #
Setхранит уникальные значения;[...new Set(arr)]— быстрый способ убрать дубликаты.Map— пары ключ–значение с любыми ключами и свойствомsize.- Для JSON и контрактов API используйте объекты;
Map/Set— для внутренней логики приложения. - Итерация
for...ofпоMapдаёт пары[key, value]. WeakMap/WeakSet— ниша для слабых ссылок без перебора всех ключей.