首页 > 解决方案 > 如何实现数据比较函数的递归?

问题描述

我的应用程序中有这个帮助函数,它告诉我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,
      },
    }

我在这里做错了什么阻止了这个测试通过?

干杯!

标签: javascripttypescriptrecursionhelperava

解决方案


这是一个非常粗略的第一次通过这样的函数(这里命名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]]为即可[]通过您的测试用例。

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

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


推荐阅读