.flatMap()

Преобразовать массив и сделать его плоским можно в два шага. Но зачем, когда можно сделать это одним.

Время чтения: 6 мин

Кратко

Скопировано

Метод flatMap() позволяет сформировать массив, применяя функцию к каждому элементу, затем уменьшает вложенность, делая этот массив плоским, и возвращает его.

Был добавлен в стандарте ES2019. Если вы поддерживаете браузеры, выпущенные раньше 2018 года, то вам понадобится полифил.

Пример

Скопировано

Получить плоский список из сложной структуры

Скопировано

Представим, что у нас есть список заказов пользователей из которого мы хотим понять, какие товары заказывают:

        
          
          const orders = [    {        id: 1,        products: [            { name: 'Чизкейк', price: 1.99 },            { name: 'Бисквит', price: 4.99 },        ]    },    {        id: 2,        products: [            { name: 'Шоколад', price: 5.59 },            { name: 'Зефир', price: 8.99 },        ]    }]
          const orders = [
    {
        id: 1,
        products: [
            { name: 'Чизкейк', price: 1.99 },
            { name: 'Бисквит', price: 4.99 },
        ]
    },
    {
        id: 2,
        products: [
            { name: 'Шоколад', price: 5.59 },
            { name: 'Зефир', price: 8.99 },
        ]
    }
]

        
        
          
        
      

Одним из решений будет сначала дважды вызвать метод map(), а затем сделать массив плоским с помощью метода flat()

Сначала воспользуемся методом map():

        
          
          orders.map(  (order) => order.products.map(product => product.name))
          orders.map(
  (order) => order.products.map(product => product.name)
)

        
        
          
        
      

Получим следующий результат:

        
          
          [['Чизкейк', 'Бисквит'], ['Шоколад', 'Зефир']]
          [['Чизкейк', 'Бисквит'], ['Шоколад', 'Зефир']]

        
        
          
        
      

Обратите внимание на вложенность массивов, нам нужно от неё избавиться. Для этого применим метод flat():

        
          
          orders  .map((order) => order.products.map((product) => product.name))  .flat()
          orders
  .map((order) => order.products.map((product) => product.name))
  .flat()

        
        
          
        
      
        
          
          ['Чизкейк', 'Бисквит', 'Шоколад', 'Зефир']
          ['Чизкейк', 'Бисквит', 'Шоколад', 'Зефир']

        
        
          
        
      

Другое решение этой задачи — сразу вызвать метод flatMap() (ведь статья у нас именно про него):

        
          
          orders.flatMap(  (order) => order.products.map(product => product.name))
          orders.flatMap(
  (order) => order.products.map(product => product.name)
)

        
        
          
        
      

Увидим что функция применилась, вложенность уменьшилась и мы получили только названия продуктов из массива products:

        
          
          ['Чизкейк', 'Бисквит', 'Шоколад', 'Зефир']
          ['Чизкейк', 'Бисквит', 'Шоколад', 'Зефир']

        
        
          
        
      

Модификация структуры данных

Скопировано

Представим, что мы пишем корзину для интернет-магазина. Товары в корзине храним в виде объекта:

        
          
          const cart = [  {    name: 'Телефон',    count: 1,    price: 500,  },  {    name: 'Ноутбук',    count: 1,    price: 800,  }]
          const cart = [
  {
    name: 'Телефон',
    count: 1,
    price: 500,
  },
  {
    name: 'Ноутбук',
    count: 1,
    price: 800,
  }
]

        
        
          
        
      

Если человек покупает телефон, то требуется добавить зарядное устройство. Для этого вызовем flatMap() и будем возвращать массив из телефона и зарядного устройства вместо одного объекта с телефоном.

        
          
          cart.flatMap(  (item) => {    if (item.name === 'Телефон') {      return [item, {        name: 'Зарядное устройство',        count: item.count,        price: 50,      }]    }    return item  })
          cart.flatMap(
  (item) => {
    if (item.name === 'Телефон') {
      return [item, {
        name: 'Зарядное устройство',
        count: item.count,
        price: 50,
      }]
    }

    return item
  }
)

        
        
          
        
      

flatMap() сделает массив плоским и мы получим массив элементов в корзине:

        
          
          [  {    name:'Телефон',    count:1,    price:500,  }, {    name:'Зарядное устройство',    count:1,    price:50,  }, {    name:'Ноутбук',    count:1,    price:800,  },]
          [
  {
    name:'Телефон',
    count:1,
    price:500,
  }, {
    name:'Зарядное устройство',
    count:1,
    price:50,
  }, {
    name:'Ноутбук',
    count:1,
    price:800,
  },
]

        
        
          
        
      

Эту же задачу можно решить и другим способом, применив метод reduce(). Но это отразится на читаемости кода, а этим пренебрегать не стоит!

        
          
          cart.reduce((list, item) => {    if (item.name === 'Телефон') {      list.push(item, {        name: 'Зарядное устройство',        count: item.count,        price: 50,      })    } else {        list.push(item)    }    return list}, [])
          cart.reduce((list, item) => {
    if (item.name === 'Телефон') {
      list.push(item, {
        name: 'Зарядное устройство',
        count: item.count,
        price: 50,
      })
    } else {
        list.push(item)
    }

    return list
}, [])

        
        
          
        
      

Как пишется

Скопировано

Как и другим похожим методам, flatMap() необходимо передать колбэк-функцию, которая будет возвращать какое-то значение. Именно это значение попадёт в итоговый массив.

Функция, которую мы передаём в flatMap(), принимает три аргумента:

  • currentValue — текущий элемент массива.
  • index — индекс текущего элемента в массиве.
  • array — массив, который мы перебираем.

Возвращает 0 или более элементов массива.

Как понять

Скопировано

Метод идентичен последовательному вызову map().flat() с параметром depth = 1, соответственно, применяется для трансформации исходных данных, с уменьшением вложенности.

Данный метод более эффективный, чем вызов этих функций по отдельности, поскольку обход массива совершается только один раз.

Разница между map() и flatMap()

Скопировано

Рассмотрим на примере разницу между методами map() и flatMap(). Возьмём массив с данными о местоположении и представим, что нужно получить (или преобразовать) координаты:

        
          
          const allActivities = [  { title: 'Офис', coordinates: [50.123, 3.291] },  { title: 'Спортзал', coordinates: [1.238, 4.292] },]const mappedCoordinates = allActivities.map(activity => activity.coordinates)const flatMappedCoordinates = allActivities.flatMap(activity => activity.coordinates)
          const allActivities = [
  { title: 'Офис', coordinates: [50.123, 3.291] },
  { title: 'Спортзал', coordinates: [1.238, 4.292] },
]

const mappedCoordinates = allActivities.map(activity => activity.coordinates)

const flatMappedCoordinates = allActivities.flatMap(activity => activity.coordinates)

        
        
          
        
      

Теперь взглянем на результаты с применением map():

        
          
          [[50.123, 3.291], [1.238, 4.292]]
          [[50.123, 3.291], [1.238, 4.292]]

        
        
          
        
      

И на результат с flatMap():

        
          
          [50.123, 3.291, 1.238, 4.292]
          [50.123, 3.291, 1.238, 4.292]

        
        
          
        
      

Вместо того чтобы использовать метод map() и получить массивы координат, а затем сократить массив, вы можете сразу использовать flatMap().

Разница между flat() и flatMap()

Скопировано

Метод flat() делает входной массив плоским, глубина указывается в аргументах.

Метод flatMap() применяет функцию, трансформируя массив и делает массив плоским. Два в одном.