我会选择这样的版本:
// 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)])
))
(如果您的环境does not support 是相对较新的Object.fromEntries,则很容易填充。)
我们可以停在那里。但值得注意的是,这个entries->map->fromEntries dance 是一个非常可重用的模式。它基本上是将函数映射到对象的属性上。也许一个好名字是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),原因在很多地方都有描述,包括Why does parseInt yield NaN with Array#map?。)
有了这个,我们现在可以写上面的sn-p了。
map 和 mapObject 等函数可以放入我们的个人实用程序库中,以便在我们应用程序的各个地方重用。 Underscore 或 Ramda 之类的库(免责声明:我是 Ramda 的作者)将这些东西收集到有用的集合中。 Ramda 的map 实际上涵盖了这两种情况,所以在 Ramda 中我们可以写成const transform = map (map (deepestValue))。
请注意,我不会基于单个案例提取这些辅助函数。如果我以前从未见过这种模式,我会对transform 的第一个版本非常满意。但是我经常做类似的事情,这种抽象对我来说是有意义的。而且我认为将事情分解成非常简单的部分总是有帮助的。