如何实现数据比较功能的递归?
How to implement recursion for data comparison function?
我的应用程序中有这个辅助函数,它告诉我 newData
与 oldData
相比的变化。
如何重构我的 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 } })
})
基本上,如果测试通过,我不希望返回任何内容,但是这个测试 returns 这个对象在失败时:
{
nested: {
- a: 1,
b: 2,
x: 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]]
更改为 []
,我相信这将通过您的测试用例。
请注意,(用户)建议的答案使用比这更好的 diff 格式:对于所有已更改的键,它包括 left
和 right
属性以提供你的价值,跳过那些根本不存在的。这会让你明确地重播或恢复差异。使用此处的格式,这并不总是有效。
这里的流程中也有太多相同输出的副本。我猜想一下,我们也许可以减少涉及的案例。
我的应用程序中有这个辅助函数,它告诉我 newData
与 oldData
相比的变化。
如何重构我的 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 } })
})
基本上,如果测试通过,我不希望返回任何内容,但是这个测试 returns 这个对象在失败时:
{
nested: {
- a: 1,
b: 2,
x: 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]]
更改为 []
,我相信这将通过您的测试用例。
请注意,left
和 right
属性以提供你的价值,跳过那些根本不存在的。这会让你明确地重播或恢复差异。使用此处的格式,这并不总是有效。
这里的流程中也有太多相同输出的副本。我猜想一下,我们也许可以减少涉及的案例。