有没有一种方法可以找到可以存在于任何对象下任何级别的键值,并使用Ramda更新它?
例如,
JSON-1
{
"query": {
"boundary": {
"type": "polygon",
"coordinates": "[[-85.33604,35.055749],[-85.33604,35.07499772909699],[-85.279134,35.07499772909699],[-85.279134,35.055749],[-85.33604,35.055749]]"
}
}
JSON-2
{
"query": {
"nearby": {
"radius": "30mi",
"coordinates": "[-121.40019800,38.55378300]"
}
}
}
在这两种JSON中,我都想做类似的事情:
query.nearby.coordinates = JSON.parse(query.nearby.coordinates)
和
query.boundary.coordinates = JSON.parse(query.boundary.coordinates)
具有单个功能。
答案 0 :(得分:2)
另一种选择是定义一个镜头,可以负责更新值。
第一种方法将假定可以找到坐标的已知路径数量有限。
// Creates a lens that accepts a list of paths and chooses the first
// matching path that exists in the target object
const somePathLens = paths => toFunctor => target => {
const path = R.find(
p => R.pathSatisfies(x => x != null, p, target),
paths
)
return R.map(
value => R.assocPath(path, value, target),
toFunctor(R.path(path, target))
)
}
// R.over can then be used with JSON.parse to parse the first
// matching path that is found.
const parseCoords = R.over(
somePathLens([
['query', 'boundary', 'coordinates'],
['query', 'nearby', 'coordinates']
]),
JSON.parse
)
console.log(parseCoords({
"query": {
"boundary": {
"type": "polygon",
"coordinates": "[[-85.33604,35.055749],[-85.33604,35.07499772909699],[-85.279134,35.07499772909699],[-85.279134,35.055749],[-85.33604,35.055749]]"
}
}
}))
console.log(parseCoords({
"query": {
"nearby": {
"radius": "30mi",
"coordinates": "[-121.40019800,38.55378300]"
}
}
}))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
如果未知实际路径,而您只需要通过给定键找到第一个值,则可以使用第二种方法。
// Recursively search for the given key in an object, returning the
// first matching path if found.
const findKeyInPath = (keyToFind, obj) => {
const findKeyInPath_ = o =>
R.has(keyToFind, o)
// if found, return this key as the path
? [keyToFind]
// otherwise find all keys with objects and recursively
// call this function.
: R.reduceRight((k, acc) => {
// find either the subpath of this key, or the subpath
// found in the remaining keys
const subPath = R.when(R.isEmpty, _ => acc, findKeyInPath_(o[k]))
// if the subpath contains a key, prepend it with the
// current key, otherwise return the empty list
return R.unless(R.isEmpty, R.prepend(k), subPath)
}, [], R.filter(k => R.propIs(Object, k, o), R.keys(o)))
return findKeyInPath_(obj)
}
// Create a lens that recursively searches for the first matching
// key within a target object.
const someKeyLens = key => toFunctor => target => {
// find the path using our new `findKeyInPath` function
const path = findKeyInPath(key, target)
return R.map(
value => R.assocPath(path, value, target),
toFunctor(R.path(path, target))
)
}
const parseCoords = R.over(
someKeyLens('coordinates'),
JSON.parse
)
console.log(parseCoords({
"query": {
"boundary": {
"type": "polygon",
"coordinates": "[[-85.33604,35.055749],[-85.33604,35.07499772909699],[-85.279134,35.07499772909699],[-85.279134,35.055749],[-85.33604,35.055749]]"
}
}
}))
console.log(parseCoords({
"query": {
"nearby": {
"radius": "30mi",
"coordinates": "[-121.40019800,38.55378300]"
}
}
}))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
值得一提的是,只有保证可以在目标对象中找到路径,这些镜头才是有效的镜头,否则行为是不确定的。
答案 1 :(得分:1)
一种方法是遍历对象树,并尝试将找到的所有字符串解析为JSON。
const parseNestedJSON = R.cond([
[R.is(String), R.tryCatch(JSON.parse, R.nthArg(1))],
[R.is(Object), obj => R.map(parseNestedJSON, obj)],
[R.T, R.identity],
])
请注意,这可能会进行一些不必要的转换,例如将{foo: '1'}
转换为{foo: 1}
(字符串到数字)。
仅定位名为coordinates
的嵌套键可能更安全:
const parseNestedJSON = R.cond([
[R.has('coordinates'), R.over(R.lensProp('coordinates'), JSON.parse)],
[R.is(Object), obj => R.map(parseNestedJSON, obj)],
[R.T, R.identity],
])
编辑:
如果coordinates
可能不是json,您也可以在此处使用tryCatch
。
[R.has('coordinates'), R.over(R.lensProp('coordinates'), R.tryCatch(JSON.parse, R.nthArg(1)))]
用法:
parseNestedJSON({"nearby": {"coordinates": "[-121.40019800,38.55378300]"}})
=> { nearby: { coordinates: [ -121.400198, 38.553783 ] } }