.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()"

Рассмотрим на примере разницу между методами 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()"

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