将函数应用于对象树中某个属性的所有实例

3
假设我有一个未知结构和深度的对象,我想要修改其中一个属性。例如,假设我想要更改的属性是id,而结构可能如下所示:
"{
  "a": "foo",
  "b": "bar",
  "c": {
    "id": {
      "a": "foo"
    },
    "b": "whatever"
  },
  "id": {
    "a": "foo"
  }
}"

是否有一种已经设定好的模式或方法,可以获取整个树中想要的键的任何实例,并对其应用通用函数?最好还可以检查数组元素是否存在。


你想要更改所有键为 id 的属性吗?你尝试过什么? - Nina Scholz
1
回答者们:请注意 [tag:functional-programming] 标签!我错过了它,结果发布了一个无用的答案。 - T.J. Crowder
@NinaScholz 是的,这就是我想做的。我已经知道如何通过递归来实现,但我想知道是否有一种特定的模式可以以函数式的方式实现,以避免在遍历对象时进行变异 - 我应该更清楚地表达这一点,因为我只在问题标签中提到了函数式编程。我正在阅读有关镜头的文章,但它们似乎不适合我的用例。 - kace91
1个回答

2

在函数式编程中,我们需要让我们的结构成为一个Functor,也就是要提供一个fmap函数,使得fmap(fn, someTree)会将所有值应用fn后返回相同的结构。有很多实现方法,例如:

let isObject = x => x && typeof x === 'object';

let map = fn => obj =>
    Array.isArray(obj)
        ? obj.map((v, k) => fn([null, v]).pop())
        : Object.fromEntries(Object.entries(obj).map(fn));

let _fmap = fn => ([key, val]) =>
    isObject(val)
        ? [
            key,
            map(_fmap(fn))(val)
        ]
        : fn([key, val]);

let fmap = fn => map(_fmap(fn));

//

const renameKey = (o, n) => ([key, val]) => (key === o ? [n, val] : [key, val]);

test = {
    a: 1,
    b: 2,
    c: [
        {a: 1}, {a: 2}, {b: 3}
    ]
};

newTest = fmap(renameKey('a', 'blah'))(test);

console.log(newTest);

在这里,我们将树的“节点”视为[键,值]对。回调函数接收一对键值对,并应该将其返回,可以进行修改也可以不进行修改。


这可能就是我在寻找的!它“几乎”可以工作,但有一个问题:对于数组,它没有返回一个数组,而是一个带有数字键的对象。编辑:它还创建了一些不应该存在的额外“0”:0属性,我不确定为什么会这样。 - kace91
还没有到那里...它正在创建一堆带有索引值和中间数组的数组(对此感到抱歉)。 - kace91
1
@kace91:没问题...能否用你的测试数据制作一个fiddle? - georg
看起来确实可以保存数组!除非我错了,我看到的限制是函数的作用域一次只能限定在一个键值上,是这样吗?也就是说,如果我想要同时涉及多个属性进行操作,那么它就行不通了——比如“仅当对象的属性B具有某个值时,修改此数组的属性A”。 - kace91
1
@kace91:没错,这个函数只会为原子(非对象)值调用回调函数。 - georg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接