如何使用Ramda更新JSON中任何级别的键值?

时间:2018-08-02 10:33:58

标签: javascript json ramda.js

有没有一种方法可以找到可以存在于任何对象下任何级别的键值,并使用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)

具有单个功能。

2 个答案:

答案 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 ] } }