首页 > 解决方案 > 无法使用递归迭代器正确迭代和解构

问题描述

我在使用RecursiveIterator时遇到问题。

基本上,我需要遍历对象图以查看可能已修改哪些属性以便能够在数据库中插入历史记录......

因此,我编写了以下 Typescript 代码以在 VS Code 中使用:

import RecursiveIterator = require("recursive-iterator");

const old = {
  vehicleId: 1,
  vehicleName: "Ol",
  modifiedProperties: [],
  device: {
    deviceId: 12,
    hardwareId: "12345",
    modifiedProperties: [],
  },
};

const n = {
  vehicleId: 1,
  vehicleName: "Ol2",
  modifiedProperties: ["vehicleName"],
  device: {
    deviceId: 12,
    hardwareId: "54321",
    modifiedProperties: ["hardwareId"],
  },
};

console.log("ouf");

var iteratorOnDeepClone = new RecursiveIterator(old, undefined, true);
var iteratorOnModifiedObject = new RecursiveIterator(n, undefined, true);

while (true) {
  let { value: valueOnModifiedObject, done: doneOnModifiedObject } =
    iteratorOnModifiedObject.next();
  let { value: valueOnDeepClone /*, done: doneOnDeepClone*/ } =
    iteratorOnDeepClone.next();

  if (valueOnDeepClone == undefined) {
    console.error("have a kitkat");
  }

  let {
    parent: parentOnDeepClone,
    node: nodeOnDeepClone,
    key: keyOnDeepClone,
    path: pathOnDeepClone,
    deep: deepOnDeepClone,
  } = valueOnDeepClone;
  let {
    parent: parentOnModifiedObject,
    node: nodeOnModifiedObject,
    key: keyOnModifiedObject,
    path: pathOnModifiedObject,
    deep: deepOnModifiedObject,
  } = valueOnModifiedObject;

  if (nodeOnModifiedObject.modifiedProperties != undefined) {
    for (var prop of nodeOnModifiedObject.modifiedProperties) {
      const oldValue: any = nodeOnDeepClone[prop];
      console.log(prop, oldValue);
    }
  }
  if (doneOnModifiedObject) {
    console.log("done!");
    break;
  }
}

现在它失败了(在第 41 行),因为在某些时候 iteratorOnDeepClone.next() 返回的对象是无效的:

Exception has occurred: TypeError: Cannot destructure property 'parent' of 'valueOnDeepClone' as it is undefined.
  at Object.<anonymous> (d:\temp\sandbox\out\iterator_sample.js:33:19)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

包.json

{
  "name": "playground",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "recursive-iterator": "^3.3.0"
  },
  "devDependencies": {
    "ts-node": "^10.2.1",
    "tsconfig-paths": "^3.11.0",
    "typescript": "4.2.4"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "out",
    "sourceMap": true
  }
}

启动.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "pwa-node",
            "request": "launch",
            "name": "Launch Program",
            "skipFiles": [
                "<node_internals>/**"
            ],
            "program": "${workspaceFolder}/iterator_sample.ts",
            "preLaunchTask": "tsc: build - tsconfig.json",
            "outFiles": [
                "${workspaceFolder}/out/**/*.js"
            ]
        }
    ]
}

编辑1:发现他休息并停下了火车。问题在于 te modifiedProperties 数组在不为空时被视为对象。所以迭代器是去同步的。

标签: javascriptrecursion

解决方案


我建议这是一种寻找差异的有问题的技术。

如果您尝试并行扫描它们,您很可能会发现仅与添加属性的顺序有关的差异,而不是它们的值。想必这不是你想要的。

用户 Mulan 有一个很好的答案,解释了一种找到差异的好方法,给你一个嵌套的结果对象,非常详细地显示事情发生了变化。这是一种稍微不同的技术,它使用- 分隔的'.'键来描绘路径来展平对象。

const compare = (a, b, first = transform (a), second = transform (b)) => 
  uniq (Object .keys (first) .concat (Object .keys (second)))
    .filter ((k) => first [k] !== second [k]) 
    .reduce ((a, k) => ((a [k] = {left: first [k], right: second [k]}), a), {})

const transform = (o) =>
  pathEntries (o) 
    .reduce ((a, [k, v]) => ((a[k .join ('.')] = v), a), {})

const pathEntries = (obj) =>
  Object (obj) === obj
    ? Object .entries (obj) .flatMap (
        ([k, x]) => pathEntries (x) .map (
          ([p, v]) => [[Array .isArray (obj) ? Number (k) : k, ... p], v]
        )
      ) 
    : [[[], obj]]

const uniq = (xs) => 
  [... new Set (xs)]

const orig = {
  foo: 42,
  bar: {baz: 99, qux: {corge: [1, 2, 3]}},
  grault: 'abc',
}

const modified = {
  foo: 42,
  bar: {baz: 99, qux: {corge: [1, 2, 4]}},
  waldo: 'xyz'
}

console .log (compare (orig, modified))
.as-console-wrapper {max-height: 100% !important; top: 0}

这通过首先转动这样的对象来工作:

const orig = {
  foo: 42,
  bar: {baz: 99, qux: {corge: [1, 2, 3]}},
  grault: 'abc',
}

进入

[
  [["foo"], 42],
  [["bar", "baz"], 99],
  [["bar", "qux", "corge", 0] ,1],
  [["bar", "qux", "corge", 1], 2],
  [["bar", "qux", "corge", 2], 3],
  [["grault"], "abc"]
]

使用效用pathEntries函数,然后进入

{
  "foo": 42,
  "bar.baz": 99,
  "bar.qux.corge.0": 1,
  "bar.qux.corge.1": 2,
  "bar.qux.corge.2": 3,
  "grault": "abc"
}

里面transform

我们对第一个和第二个对象执行此操作,然后只需在输出对象中找到它们不同的键,并在单个对象结构中报告两个对象之间的不同值。


花木兰的技术更灵活,以更有条理的方式捕捉差异。但是,根据您拥有的 db 样式,这个平面输出可能更适合存储在数据库中。


推荐阅读