【问题标题】:How to implement recursion for data comparison function?如何实现数据比较函数的递归?
【发布时间】:2021-06-27 14:28:52
【问题描述】:

我的应用中有这个帮助函数,它告诉我newDataoldData 相比的变化。

如何重构我的 getChanges 函数以使下面的测试通过? 我想我可能需要让这个函数递归,因为它是从自身内部执行的,但我不完全确定如何实现。

看起来像这样:

getChanges 辅助函数:

export function getChanges(oldData: Record<string, any>, newData: Record<string, any>): any {
  
  return Object.entries(newData).reduce((changes, [key, newVal]) => {

    if (JSON.stringify(oldData[key]) === JSON.stringify(newVal)) return changes
    changes[key] = newVal
    return changes

  }, {} as any)
}

在我的实际测试中,我使用 ava 的 deepEqual 来帮助进行比较。 不管出于何种原因,我正在运行的一项测试没有通过。

index.ts 测试 1 通过

import test from 'ava'
import { getChanges } from '../src/comparisonHelpers.js'

test('getChanges - flat', (t) => {
  const a = getChanges({}, {})
  const b = {}
  t.deepEqual(a, b)

  t.deepEqual(getChanges({ a: 1 }, { a: 1 }), {})
  t.deepEqual(getChanges({ a: 1 }, {}), {})
  t.deepEqual(getChanges({}, { a: 1 }), { a: 1 })

  const oldData = { a: 1, b: 1, c: 1 }
  const newData = { x: 1, a: 1, b: 2 }
  const result = getChanges(oldData, newData)
  const expect = { x: 1, b: 2 }
  t.deepEqual(result, expect)
})

index.ts 测试 2 未通过

import test from 'ava'
import { getChanges } from '../src/comparisonHelpers.js'

test('getChanges - nested difference', (t) => {
  const oldData = { nested: { a: 1, b: 1, c: 1 } }
  const newData = { nested: { x: 1, a: 1, b: 2 } }
  const res = getChanges(oldData, newData)
  t.deepEqual(res, { nested: { x: 1, b: 2 } })
})

基本上,如果测试通过,我希望不会返回任何内容,但是此测试会在失败时返回此对象:

{
      nested: {
  -     a: 1,
        b: 2,
        x: 1,
      },
    }

我做错了什么导致此测试无法通过?

干杯!

【问题讨论】:

  • 你的期望值从哪里来?它不应该包含c: undefined之类的东西吗?
  • 我认为您将从this Q&A 中受益。如果您有任何问题,请告诉我。
  • @ScottSauyet 感谢您的回复!现在,这些测试完全与它们所在的文件隔离。现在,预期值是 getChanges 函数中的第二个值(参见我添加的“测试 1”)。测试 1 将事情分解得更好,因为它只是在寻找两者之间的差异。我希望编辑比我的解释更有意义
  • @Thankyou:我正在寻找这个答案。没找到,自己写了第一遍。不过还是太丑了,无法分享。
  • @LovelyAndy:我试图弄清楚为什么你的输出中没有c?它在第一个值而不是第二个值中。我希望像{nested: {x: 1, b: 2, c: undefined}} 或类似的东西。删除c 肯定是一个应该注意的变化,不是吗?但是请查看Thankyou 的建议,以获得更强大的样式差异。

标签: javascript typescript recursion helper ava


【解决方案1】:

这是一个非常粗略的第一次这样的函数(这里命名为diff而不是getChanges):

const isObject = (o) => 
  Object (o) === o
const isEmptyObject = (o) => 
  isObject(o) && Object .keys (o) .length == 0

const diff = (a, b) => 
  Object .fromEntries (
    [... (new Set ([...Object .keys (a), ...Object.keys(b)]))].flatMap (
      (k) =>
        k in a
          ? k in b
            ? isObject (a [k])
              ? isObject (b [k])
                ? [[k, diff (a [k], b [k])]]  // <--- recursive call here
                : [[k, b [k]]]
              : a[k] == b [k]
                ? []
                : [[k, b [k]]]
            : [[k, undefined]]
          : [[k, b [k]]]
    ) .filter (([k, v]) => !isEmptyObject(v))
  )


const oldData = {nested: { a: 1, b: 1, c: 1 }, foo: {x: 3, y: 5}, bar: {x: 1}, qux: {x: 6}}
const newData = {nested: { x: 1, a: 1, b: 2 }, foo: {x: 4, y: 5}, bar: {x: 1}, corge: {x: 6}}

console .log (diff (oldData, newData))
.as-console-wrapper {max-height: 100% !important; top: 0}

这是非常简单的,并且有些输入不起作用,尤其是那些故意包含undefined 值的输入。它还可以通过设计包含新数据中缺少的键的undefined 值。但是不包括它们很容易:只需在函数中将[[k, undefined]] 更改为[],我相信这将通过您的测试用例。

请注意,Thank you(用户)建议的答案使用比这更好的差异格式:对于所有已更改的键,它包括 leftright 属性来为您提供值,跳过那些简单的不存在。这将使您明确地重播或还原差异。使用此处的格式,这并不总是有效。

这里的流程中也有太多相同输出的副本。我猜想,我们也许可以减少涉及的案件。

【讨论】:

  • 非常感谢斯科特。这比我最初预期的要多得多,但老实说帮助很大。感谢您抽出宝贵的时间,希望我能完成这项工作!再次感谢!
  • 很好地打破了这种方法的缺点。尽管如此,这是一个很好的答案。我无法对其进行测试,但它看起来也可以将数组转换为对象。
  • @Thankyou:我也没有测试过,但我的意思是说不明确处理数组是另一个限制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-13
  • 2011-08-27
  • 2016-04-28
  • 1970-01-01
  • 2017-11-26
  • 2017-11-13
相关资源
最近更新 更多