← JavaScript/Object.keys, values, entries и fromEntries#111 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Object.keys, values, entries и fromEntries

Представь: ты получил с сервера объект-прайс-лист и должен поднять все цены на 20%, убрать позиции дешевле 100 рублей и вернуть обратно объект того же формата. Без Object.entries тебе нужен for...in с ручными проверками. С entries + map + fromEntries это одна читаемая цепочка.

На основе предыдущих уроков

  • «Массивы» — методы .map(), .filter(), .reduce(), которые теперь применяются к парам [ключ, значение]
  • «Объекты» — свойства объекта, перебор через for...in
  • «Деструктуризация» — деструктуризация ([key, value]) в аргументе колбэка
  • «Map/Set» — Object.fromEntries принимает Map напрямую
  • Object.keys() — массив ключей

    const product = { name: 'Ноутбук', price: 75000, stock: 12 }
    
    Object.keys(product)    // ['name', 'price', 'stock']

    Object.values() — массив значений

    Object.values(product)  // ['Ноутбук', 75000, 12]
    
    // Сумма всех значений:
    const prices = { coffee: 250, sandwich: 350, juice: 180 }
    const total = Object.values(prices).reduce((sum, p) => sum + p, 0)
    // 780

    Object.entries() — массив пар [ключ, значение]

    Object.entries(product)
    // [['name', 'Ноутбук'], ['price', 75000], ['stock', 12]]
    
    // Удобно деструктурировать в for...of
    for (const [key, value] of Object.entries(product)) {
      console.log(`${key}: ${value}`)
    }
    // name: Ноутбук
    // price: 75000
    // stock: 12

    Object.fromEntries() — объект из массива пар

    Object.fromEntries — обратная операция к Object.entries. Принимает итерируемый объект пар [ключ, значение] и возвращает новый объект:

    const entries = [['name', 'Ноутбук'], ['price', 75000]]
    Object.fromEntries(entries)  // { name: 'Ноутбук', price: 75000 }

    Трансформация объекта: entries → map/filter → fromEntries

    Главная сила этих методов — возможность применять операции массивов к объектам:

    const prices = { яблоко: 80, банан: 50, манго: 300 }
    
    // Увеличить все цены на 20%
    const updated = Object.fromEntries(
      Object.entries(prices).map(([item, price]) => [item, price * 1.2])
    )
    // { яблоко: 96, банан: 60, манго: 360 }
    
    // Оставить только дорогие товары
    const expensive = Object.fromEntries(
      Object.entries(prices).filter(([, price]) => price >= 100)
    )
    // { манго: 300 }

    Object.fromEntries принимает Map

    Object.fromEntries принимает объект Map, что позволяет конвертировать Map в обычный объект:

    const map = new Map([['login', 'admin'], ['role', 'superuser']])
    
    // Map → объект
    const obj = Object.fromEntries(map)
    // { login: 'admin', role: 'superuser' }
    
    // объект → Map
    const backToMap = new Map(Object.entries(obj))

    Типичные ошибки

    1. Перебор через `for...in` вместо `Object.entries` — захватывает унаследованные свойства:

    function Base() {}
    Base.prototype.inherited = 'да'
    const obj = new Base()
    obj.own = 'моё'
    
    // Плохо: for...in обходит inherited тоже
    for (const key in obj) {
      console.log(key)  // 'own', 'inherited' — унаследованное попало!
    }
    
    // Хорошо: Object.keys/entries — только собственные свойства
    for (const [key, val] of Object.entries(obj)) {
      console.log(key)  // 'own'
    }

    2. Попытка трансформировать объект через `.map()` напрямую:

    const prices = { coffee: 250, tea: 80 }
    
    // Ошибка: объекты не имеют .map()
    // prices.map(...)  // TypeError: prices.map is not a function
    
    // Правильно: сначала entries, потом map, потом fromEntries
    const doubled = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => [k, v * 2])
    )
    // { coffee: 500, tea: 160 }

    3. Забыть вернуть пару из `.map()` — fromEntries получает `undefined`:

    // Плохо: нет явного return в стрелочной функции с фигурными скобками
    const result = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => {
        v * 2  // нет return!
      })
    )
    // { coffee: undefined, tea: undefined }
    
    // Хорошо: возвращаем пару
    const result2 = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => [k, v * 2])  // неявный return
    )

    В реальных проектах

  • Нормализация API-ответа — переименование ключей, преобразование типов (строки в числа)
  • Конфигурация переменных окружения — Object.fromEntries(Object.entries(process.env).filter(...))
  • Агрегация статистики — Object.fromEntries собирает результаты reduce в новый объект
  • i18n/локализация — трансформация плоского объекта переводов в иерархический
  • Примеры

    Трансформация прайс-листа: наценка, фильтрация и конвертация Map

    const catalog = {
      'Смартфон Samsung A54': 32000,
      'Наушники Sony':        8500,
      'Чехол силиконовый':    450,
      'Зарядка 65W':          2100,
      'Кабель USB-C':         390,
    }
    
    // 1. Поднять все цены на 20%
    const withMarkup = Object.fromEntries(
      Object.entries(catalog).map(([name, price]) => [name, Math.round(price * 1.2)])
    )
    console.log('После наценки:')
    console.log(withMarkup)
    // { 'Смартфон Samsung A54': 38400, 'Наушники Sony': 10200, 'Чехол силиконовый': 540, ... }
    
    // 2. Оставить только товары дороже 1000 рублей
    const premium = Object.fromEntries(
      Object.entries(catalog).filter(([, price]) => price > 1000)
    )
    console.log('\nДорогие товары:')
    console.log(premium)
    // { 'Смартфон Samsung A54': 32000, 'Наушники Sony': 8500, 'Зарядка 65W': 2100 }
    
    // 3. Сумма всего каталога
    const total = Object.values(catalog).reduce((sum, p) => sum + p, 0)
    console.log('\nИтого в каталоге:', total, 'руб.')
    // Итого в каталоге: 43440 руб.
    
    // 4. Конвертация Map → объект (например, скидки пришли как Map)
    const discounts = new Map([
      ['Смартфон Samsung A54', 0.1],
      ['Наушники Sony', 0.05],
    ])
    const discountObj = Object.fromEntries(discounts)
    console.log('\nСкидки:', discountObj)
    // { 'Смартфон Samsung A54': 0.1, 'Наушники Sony': 0.05 }
    
    // 5. Применить скидки к каталогу
    const withDiscounts = Object.fromEntries(
      Object.entries(catalog).map(([name, price]) => {
        const discount = discountObj[name] ?? 0
        return [name, Math.round(price * (1 - discount))]
      })
    )
    console.log('\nС учётом скидок:', withDiscounts)

    Object.keys, values, entries и fromEntries

    Представь: ты получил с сервера объект-прайс-лист и должен поднять все цены на 20%, убрать позиции дешевле 100 рублей и вернуть обратно объект того же формата. Без Object.entries тебе нужен for...in с ручными проверками. С entries + map + fromEntries это одна читаемая цепочка.

    На основе предыдущих уроков

  • «Массивы» — методы .map(), .filter(), .reduce(), которые теперь применяются к парам [ключ, значение]
  • «Объекты» — свойства объекта, перебор через for...in
  • «Деструктуризация» — деструктуризация ([key, value]) в аргументе колбэка
  • «Map/Set» — Object.fromEntries принимает Map напрямую
  • Object.keys() — массив ключей

    const product = { name: 'Ноутбук', price: 75000, stock: 12 }
    
    Object.keys(product)    // ['name', 'price', 'stock']

    Object.values() — массив значений

    Object.values(product)  // ['Ноутбук', 75000, 12]
    
    // Сумма всех значений:
    const prices = { coffee: 250, sandwich: 350, juice: 180 }
    const total = Object.values(prices).reduce((sum, p) => sum + p, 0)
    // 780

    Object.entries() — массив пар [ключ, значение]

    Object.entries(product)
    // [['name', 'Ноутбук'], ['price', 75000], ['stock', 12]]
    
    // Удобно деструктурировать в for...of
    for (const [key, value] of Object.entries(product)) {
      console.log(`${key}: ${value}`)
    }
    // name: Ноутбук
    // price: 75000
    // stock: 12

    Object.fromEntries() — объект из массива пар

    Object.fromEntries — обратная операция к Object.entries. Принимает итерируемый объект пар [ключ, значение] и возвращает новый объект:

    const entries = [['name', 'Ноутбук'], ['price', 75000]]
    Object.fromEntries(entries)  // { name: 'Ноутбук', price: 75000 }

    Трансформация объекта: entries → map/filter → fromEntries

    Главная сила этих методов — возможность применять операции массивов к объектам:

    const prices = { яблоко: 80, банан: 50, манго: 300 }
    
    // Увеличить все цены на 20%
    const updated = Object.fromEntries(
      Object.entries(prices).map(([item, price]) => [item, price * 1.2])
    )
    // { яблоко: 96, банан: 60, манго: 360 }
    
    // Оставить только дорогие товары
    const expensive = Object.fromEntries(
      Object.entries(prices).filter(([, price]) => price >= 100)
    )
    // { манго: 300 }

    Object.fromEntries принимает Map

    Object.fromEntries принимает объект Map, что позволяет конвертировать Map в обычный объект:

    const map = new Map([['login', 'admin'], ['role', 'superuser']])
    
    // Map → объект
    const obj = Object.fromEntries(map)
    // { login: 'admin', role: 'superuser' }
    
    // объект → Map
    const backToMap = new Map(Object.entries(obj))

    Типичные ошибки

    1. Перебор через `for...in` вместо `Object.entries` — захватывает унаследованные свойства:

    function Base() {}
    Base.prototype.inherited = 'да'
    const obj = new Base()
    obj.own = 'моё'
    
    // Плохо: for...in обходит inherited тоже
    for (const key in obj) {
      console.log(key)  // 'own', 'inherited' — унаследованное попало!
    }
    
    // Хорошо: Object.keys/entries — только собственные свойства
    for (const [key, val] of Object.entries(obj)) {
      console.log(key)  // 'own'
    }

    2. Попытка трансформировать объект через `.map()` напрямую:

    const prices = { coffee: 250, tea: 80 }
    
    // Ошибка: объекты не имеют .map()
    // prices.map(...)  // TypeError: prices.map is not a function
    
    // Правильно: сначала entries, потом map, потом fromEntries
    const doubled = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => [k, v * 2])
    )
    // { coffee: 500, tea: 160 }

    3. Забыть вернуть пару из `.map()` — fromEntries получает `undefined`:

    // Плохо: нет явного return в стрелочной функции с фигурными скобками
    const result = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => {
        v * 2  // нет return!
      })
    )
    // { coffee: undefined, tea: undefined }
    
    // Хорошо: возвращаем пару
    const result2 = Object.fromEntries(
      Object.entries(prices).map(([k, v]) => [k, v * 2])  // неявный return
    )

    В реальных проектах

  • Нормализация API-ответа — переименование ключей, преобразование типов (строки в числа)
  • Конфигурация переменных окружения — Object.fromEntries(Object.entries(process.env).filter(...))
  • Агрегация статистики — Object.fromEntries собирает результаты reduce в новый объект
  • i18n/локализация — трансформация плоского объекта переводов в иерархический
  • Примеры

    Трансформация прайс-листа: наценка, фильтрация и конвертация Map

    const catalog = {
      'Смартфон Samsung A54': 32000,
      'Наушники Sony':        8500,
      'Чехол силиконовый':    450,
      'Зарядка 65W':          2100,
      'Кабель USB-C':         390,
    }
    
    // 1. Поднять все цены на 20%
    const withMarkup = Object.fromEntries(
      Object.entries(catalog).map(([name, price]) => [name, Math.round(price * 1.2)])
    )
    console.log('После наценки:')
    console.log(withMarkup)
    // { 'Смартфон Samsung A54': 38400, 'Наушники Sony': 10200, 'Чехол силиконовый': 540, ... }
    
    // 2. Оставить только товары дороже 1000 рублей
    const premium = Object.fromEntries(
      Object.entries(catalog).filter(([, price]) => price > 1000)
    )
    console.log('\nДорогие товары:')
    console.log(premium)
    // { 'Смартфон Samsung A54': 32000, 'Наушники Sony': 8500, 'Зарядка 65W': 2100 }
    
    // 3. Сумма всего каталога
    const total = Object.values(catalog).reduce((sum, p) => sum + p, 0)
    console.log('\nИтого в каталоге:', total, 'руб.')
    // Итого в каталоге: 43440 руб.
    
    // 4. Конвертация Map → объект (например, скидки пришли как Map)
    const discounts = new Map([
      ['Смартфон Samsung A54', 0.1],
      ['Наушники Sony', 0.05],
    ])
    const discountObj = Object.fromEntries(discounts)
    console.log('\nСкидки:', discountObj)
    // { 'Смартфон Samsung A54': 0.1, 'Наушники Sony': 0.05 }
    
    // 5. Применить скидки к каталогу
    const withDiscounts = Object.fromEntries(
      Object.entries(catalog).map(([name, price]) => {
        const discount = discountObj[name] ?? 0
        return [name, Math.round(price * (1 - discount))]
      })
    )
    console.log('\nС учётом скидок:', withDiscounts)

    Задание

    Интернет-магазин хранит остатки товаров как объект с числовыми значениями. Напиши функцию filterByValue(obj, threshold), которая возвращает новый объект, содержащий только свойства со значением строго больше threshold. Используй Object.entries, .filter() и Object.fromEntries.

    Подсказка

    Object.entries(obj) даёт массив [ключ, значение]. Применяй .filter(([key, value]) => value > threshold) — деструктурируй пару прямо в параметре. Затем Object.fromEntries(...) соберёт результат обратно в объект.

    Загружаем среду выполнения...
    Загружаем AI-помощника...