使用 Ramda 或 vanillaJS 将任意深度嵌套的值转换为对象键

Convert an arbitrarily deeply nested value into an object key using Ramda or vanillaJS

我正在使用一个库,该库以一种非常规的方式 returns 一系列值。库 returns 嵌套对象不是返回数组,而是将最终叶节点作为值包含在内。例如:

red.walk.fast.true 变为 {red: {walk: {fast: 'true'}}} 要么 climbing.ascending 变为 {climbing: 'ascending'}

嵌套对象的格式实际上适合我的用例,但我需要一种将最终值转换为另一个嵌套对象(具有空值 - 但这并不重要)而不是值的方法。所以:

{red: {walk: {fast: 'true'}}} 变为 {red: {walk: {fast: {true: null}}}}

该公式需要适用于任意深度嵌套的对象。不过,我正在努力获得与 Ramda 一起使用的递归函数。我认为以下方法可行:

const stringPropToObject = R.ifElse(
    R.is(String),
    R.assoc(R.__, null, {}),
    mapAllStringPropsToObjs);

const mapAllStringPropsToObjs = R.map(stringPropToObject)

const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log(updatedObject);

mapAllStringPropsToObjs(尝试)将对象属性映射到 stringPropToObject。然后查看传入的 属性。

这导致 "Cannot access 'mapAllStringPropsToObjs' before initialization"。我理解为什么我会在当前的声明顺序中收到此错误,但我不知道如何命令它们避免它——它们是相互依赖的函数。

任何人都知道我可以使用 Ramda 或 vanilla JS 做什么来转换: {climbing: 'ascending'} 变成 {climbing: {ascending: null}}{red: {walk: {fast: 'true'}}} 变成 {red: {walk: {fast: {true: null}}}} 或任何其他任意嵌套值。

提前感谢您的任何建议。

您可以迭代条目,如果没有对象,则分配一个新对象。

function f(object) {
    Object.entries(object).forEach(([k, v]) => {
        if (v && typeof v === 'object') f(v);
        else object[k] = { [v]: null };
    });
}

var object = { red: { walk: { fast: 'true' } } };

f(object);
console.log(object);

这个解决方案是递归的,它创建一个新的转换对象而不是改变旧的。

function transformObject(obj, final = {}, curr = final) {
  Object.entries(obj).forEach(([key, value]) => {
    curr[key] = {}
    if (typeof value !== 'object') curr[key][value] = null
    else transformObject(value, final, curr[key])
  })
  return final
}

const dataOne = {climbing: {run: 'slow', hike: {mountain: 'fast'}}}
const dataTwo = {red: {walk: {fast: 'true'}}}

console.log(dataOne, transformObject(dataOne))
console.log(dataTwo, transformObject(dataTwo))

解决相互依赖函数问题的最简单方法是简单地将第一个中的引用替换为第二个中的 lambda:

const stringPropToObject = ifElse(
  is(String),
  assoc(__, null, {}),
  x => mapAllStringPropsToObjs(x)
);

const mapAllStringPropsToObjs = map(stringPropToObject)


const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log (updatedObject)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {ifElse, is, assoc, __, map} = R                               </script>

Ramda 的设计并不是特别适合递归,但这当然是语言中的一个基本问题。

我可能会用不同的方式写这个,用普通的 JS,像这样:

const transform = (obj) => 
  Object .entries (obj) 
    .reduce ((a, [k, v]) => ({
      ...a,
      [k]: typeof v == 'string' ? {[v]: null} : transform (v)
    }), {})

但我认为你的递归 Ramda 解决方案通过这个小调整更简单、更清晰。

如@ScottSauyet 所述,您可以使用函数(匿名或箭头)在将来实际定义时调用 mapAllStringPropsToObjs()

执行延迟计算的一段代码称为thunk。根据wikipedia:

In computer programming, a thunk is a subroutine used to inject an additional calculation into another subroutine. Thunks are primarily used to delay a calculation until its result is needed, or to insert operations at the beginning or end of the other subroutine.

此外,我还将R.assoc替换为翻转的R.objOf来生成对象。

const objOfNull = flip(objOf)(null);

const mapAllStringPropsToObjs = map(ifElse(
  is(String),
  objOfNull,
  x => mapAllStringPropsToObjs(x)
));

const updatedObject = mapAllStringPropsToObjs({ a: { b: { c: 'd' } } })

console.log (updatedObject)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {map, ifElse, is, flip, objOf} = R                               </script>