What are the steps to incorporate recursion into a data comparison function?

I have a function in my application that helps me identify the changes between new data and old data.

I am looking to refactor my getChanges function so that a particular test can pass successfully. I believe making this function recursive might be necessary since it calls itself from within, but I am unsure of how to approach this.

The current implementation is as follows:

getChanges helper function:

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)
}

When running tests, I use ava's deepEqual method for comparison purposes. However, one of my tests is not passing as expected.

Test 1 in index.ts passes

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)
})

Test 2 in index.ts does not pass

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 } })
})

In case of failure, the test returns the following unexpected output:

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

Any guidance on what could be causing this test to fail would be greatly appreciated.

Thank you!

Answer №1

Below is an initial attempt at creating a function to compare data structures, named diff:

const checkObject = (obj) => 
  Object(obj) === obj
const isObjEmpty = (obj) => 
  checkObject(obj) && Object.keys(obj).length == 0

const diff = (dataA, dataB) => 
  Object.fromEntries(
    [...new Set([...Object.keys(dataA), ...Object.keys(dataB)])].flatMap (
      (key) =>
        key in dataA
          ? key in dataB
            ? checkObject(dataA[key])
              ? checkObject(dataB[key])
                ? [[key, diff(dataA[key], dataB[key])]]  
                : [[key, dataB[key]]]
              : dataA[key] == dataB[key]
                ? []
                : [[key, dataB[key]]]
            : [[key, undefined]]
          : [[key, dataB[key]]]
    ).filter(([key, value]) => !isObjEmpty(value))
  )

const prevData = {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(prevData, newData))
.as-console-wrapper {max-height: 100% !important; top: 0}

This approach is basic and may not handle all scenarios, particularly those with explicit undefined values. It also includes undefined for missing keys in the new data. To exclude them, simply change [[key, undefined]] to [] in the function. Updating this would likely fulfill your test cases.

User provided a more organized diff format suggestion in this response, which distinguishes between changed keys with left and right properties. This enables clear replay or reversal of differences. The current format may not always support this functionality.

This implementation generates too many repeated outputs. Refinement could reduce these redundancies with careful consideration.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The browser displays the jQuery ajax response instead of passing it to the designated callback function

I have a web application that connects to another web service and completes certain tasks for users. Using codeigniter and jQuery, I have organized a controller in codeigniter dedicated to managing ajax requests. The controller executes necessary actions ...

Problems Arise Due to HTA File Cache

My JavaScript function fetches the value of a label element first, which serves as an ID for a database entry. These IDs are then sent to an ASP page to retrieve the save location of images from the database. The save location information for each selecte ...

Tips for concealing a Bootstrap modal during the show.bs.modal event

Check out this code snippet: let order_modal = document.getElementById('order'); order_modal.addEventListener('show.bs.modal', function (e) { bootstrap.Modal.getInstance(order_modal).hide(); }, false); Upon opening the modal, the ...

What is the most efficient method for creating around 500 small images, using either JavaScript or server-side C?

Embarking on my initial endeavor to create images dynamically. The goal is to present approximately 500 small images, each 32px X 24px in size with 16 colors, placed within table cells. Each image consists of a 2D array of colored pixels, with values prov ...

Tips for sending a command to the server using the ssh2-promise package in Node.js

In my current code, I am utilizing an SSH library to establish a connection to a server, create a shell session, and send a password to authenticate. However, I am encountering an issue where the password is not being sent as intended. Upon some debugging ...

The Webix text area does not adapt to different screen sizes

I'm just starting out with Webix. I have created a form that includes a textarea. webix.ui({ rows:[ { view:"form", id:"log_form", elements:[ { view:"textarea" ,height:700}, { margin:5, cols:[ { view:"button", value:"Save" , type:"form" }, { view:"b ...

jQuery Problem: Iterating over each element and executing a function

Currently facing an issue with running a function through .each in jQuery. It doesn't seem to be selecting the element properly using 'this'. Here is the code: (Edit: I am trying to center elements absolutely regardless of screen width, for ...

Tips for creating an automatic interval timer reset feature

I searched for similar questions but couldn't find any that helped me. With the assistance of some kind individuals from Stack Overflow, I was able to implement an interval timer sequence on my website. This trailer automatically displays work example ...

Angular is encountering an issue where double braces are not being evaluated and are instead being passed through

I'm working on an angular project and have the following code snippet: <div vh-accordion-group id="{{$index}}" panel-class="panel-info"> <div vh-accordion-header> </div> <div vh-accordion-body> </div> </div> ...

Issue with dynamic HTML preventing Bootstrap tooltip functionality

There's an HTML page where a section is dynamically generated through HTML injection from a typescript angularjs controller using $sce and ng-bind-html. The issue is that the custom bootstrap tooltip style doesn't seem to be applied, and only t ...

Filtering an unsorted list through the URL parameter of #value when the page is first

I am currently working on building a portfolio website and have encountered some difficulties with filtering the portfolio elements using Javascript / Jquery. Since I am new to this, I am keeping things simple by implementing a basic filter that shows or h ...

Decoding the `this` Mystery in VueJS

Recently, I decided to delve into the world of VueJS starting from square one. Following their official guide has been a helpful resource, but I've hit a roadblock at this section. One particular example in the guide caught my attention... var app5 = ...

Guide on importing a JSON file into a datatable

Recently delving into Jquery, I have a small task ahead of me involving loading json files to a datatable using jquery ui dialog‎. Currently, I am utilizing Visual Studio 2010 for this specific project. Here is the JSON code snippet: { "RELATIONMboSet" ...

Convert the generic primitive type to a string

Hello, I am trying to create a function that can determine the primitive type of an array. However, I am facing an issue and haven't been able to find a solution that fits my problem. Below is the function I have written: export function isGenericType ...

Is there a way to trigger a function with the onclick event in NextJs?

Working on my NestJS project, I am still a beginner in the field. My current task involves creating a web application that displays a leaflet map with clickable tiles. Each tile should have a button that triggers a specific function when clicked. However, ...

TypeScript overloading error: Anticipated 0 parameters, received 2 instead

I am facing an issue with a class containing an overloaded method that has two versions. One version does not take any arguments, while the second one can take two arguments. class DFD { ... getEndDatetime(): string; getEndDatetime(startTime?: ...

What is the most effective method for displaying an error code when a JavaScript error occurs?

I'm currently dealing with a library that is throwing errors: throw new Error('The connection timed out waiting for a response') This library has the potential to throw errors for various reasons, making it challenging for users to handle ...

Create a directive for AngularJS that utilizes SVG elements without using the deprecated

I rely heavily on directives for creating and manipulating intricate SVGs. With the deprecation of "replace" in directive factories starting from version 1.3.??, I am facing a dilemma on how to construct a valid SVG without utilizing replace: true in my di ...

Utilize Ajax datatable to showcase information in a visually interactive format

I've been grappling with this problem for an entire day. Essentially, I have a table and I need to pass data in a multidimensional array $list through a datatable using AJAX. This way, I can JSON encode it and send it back for display: $('#table ...

Incorporating the "+ " icon in Vuejs Dropzone to indicate the presence of existing images

Looking to enhance my Vue-dropzone file uploader component by adding an icon or button labeled "Add more images" when there are already images present in the dropzone. This will help users understand that they can upload multiple photos. Any suggestions on ...