Я бы выбрал версию, подобную этой:
// helper functions
const mapObject = (fn) => (o) => Object .fromEntries (
Object .entries (o) .map (([k, v]) => [k, fn (v)])
)
const map = (fn) => (xs) => xs .map(x => fn (x))
// main functions
const deepestValue = (o) =>
typeof o .value== "object" ? deepestValue (o .value) : o .value
const transform = map (mapObject (deepestValue))
// demo
const input=[{accountName: {type: "link", value: {value: "1234567890123456789", to: "/q?Id=1237896540789654"}}, bank: {type: "text", value: "Foo Bank"}}, {accountName: {type: "link", value: {value: "9234567890123456789", to: "/q?Id=9234567890123456789"}}, bank: {type: "text", value: "Foo Bank"}}]
console.log (transform (input))
Мы можем построить это поэтапно:
Мы начнем с написания простой рекурсивной deepestValue
функции, подобной этой:
const deepestValue = (o) =>
typeof o .value== "object" ? deepestValue (o .value) : o .value
Это можно использовать для написания нашей функции преобразования следующим образом:
const transform = (xs) => xs .map (x => Object.fromEntries (
Object .entries (x) .map (([k, v]) => [k, deepestValue (v)])
))
(Если ваша среда не поддерживает относительно новый Object.fromEntries
, это очень легко подобрать.)
Мы могли бы остановиться там. Но стоит отметить, что этот танец entries
-> map
-> fromEntries
очень многократно используется. Это в основном отображение функции по свойствам объекта. Возможно, хорошее имя mapObject
. Мы можем просто извлечь это и использовать его так:
const mapObject = (fn) => (o) => Object .fromEntries (
Object .entries (o) .map (([k, v]) => [k, fn (v)])
)
const transform = (xs) => xs .map (mapObject (deepestValue))
Но есть еще одна функция, которую мы могли бы абстрагировать от этого, функция map
, которая работает как Array.prototype.map
, но которая является подставкойодиночная функция. Это также очень легко написать.
const map = (fn) => (xs) => xs .map (x => fn (x))
(я не пишу просто (fn) => (xs) => xs .map (fn)
по причинам, описанным во многих местах, в том числе в Почему parseInt дает NaN с Array # map? .)
Теперь мы можем написать приведенный выше фрагмент кода.
Такие функции, как map
и mapObject
, могут быть добавлены в наши библиотеки личных утилит для повторного использования в различных местах внаше приложение. Такие библиотеки, как Underscore или Ramda (отказ от ответственности: я автор Ramda) собирают такие вещи в полезные коллекции. Рамда map
на самом деле охватывает оба этих случая, поэтому в Рамде мы могли бы написать const transform = map (map (deepestValue))
.
Обратите внимание, что я не буду извлекать эти вспомогательные функции на основе единичных случаев. Если бы я никогда не видел шаблон раньше, я был бы совершенно счастлив с первой версией transform
. Но я делал подобные вещи достаточно часто, чтобы эта абстракция имела для меня смысл. И я думаю, что это всегда помогает разбить вещи на очень простые кусочки.