Explore RxJs DistinctUntilChanged for Deep Object Comparison

I have a scenario where I need to avoid redundant computations if the subscription emits the same object.

this.stateObject$
  .pipe(distinctUntilChanged((obj1, obj2) => JSON.stringify({ obj: obj1 }) === JSON.stringify({ obj: obj2 })))
.subscribe(obj => heavyComputeTask(obj))

The current implementation may fail if the order of keys changes and is not efficient either.

How can I optimize this code?

In addition, to prevent repeating the same code, I intended to create a custom filter pipe like this:

export function ignoreUnChanged<T>(source: Observable<T>): Observable<T> {
  return source.pipe(
    distinctUntilChanged((obj1, obj2) => JSON.stringify({ obj: obj1 }) === JSON.stringify({ obj: obj2 })
  ))
}

However, TypeScript throws an error stating that 'MonoTypeOperatorFunction<T>' is not compatible with 'OperatorFunction<T, T>'. Is there a more effective way to resolve this issue?

Thank you in advance.

Answer №1

If your use-case is straightforward and you don't require the option to configure ignoreUnChanged, then you can simply implement the following solution:

const ignoreUnChangedSimple = distinctUntilChanged((obj1, obj2) => JSON.stringify({ obj: obj1 }) === JSON.stringify({ obj: obj2 }));

...

source$
  .pipe(
    ignoreUnChangedSimple,
  )
  .subscribe(console.log);

There are a few more intricate variations, with the most complex (yet flexible) one looking like this:

export function ignoreUnChanged3<T>(someParams: any): MonoTypeOperatorFunction<T> {
  return (source: Observable<T>) => source.pipe(
    distinctUntilChanged((obj1, obj2) =>
      JSON.stringify({ obj: obj1 }) === JSON.stringify({ obj: obj2 })
    ),
  );
}

Check out the live demo here: https://stackblitz.com/edit/rxjs-teaakn

Answer №2

If you're looking for a more efficient way to compare deep objects, I recommend trying out the lodash operator isEqual. Personally, it has been effective for me in the past.

On another note, I can provide assistance with creating custom RxJS operators. A great resource for this is a talk by Ben Lesh and Tracy Lee from ng-conf 2019, which has inspired many of the custom operators I've developed.

A key aspect missing from your code is the necessity to return a new Observable. Observables pipes function purely, so you would need something like this:

export function ignoreUnChanged<T>() {
return (source: Observable<T>) =>
    new Observable<T>(subscriber => {
        return source
            .pipe(
                distinctUntilChanged((obj1, obj2) => JSON.stringify({ obj: obj1 }) === JSON.stringify({ obj: obj2 })
            )
            .subscribe({
                next: v => subscriber.next(v),
                error: e => subscriber.error(e),
                complete: () => subscriber.complete()
            });
    });
}

An implementation using this method would look something like:

this.stateObject$.pipe(ignoreUnChanged()).subscribe(obj => heavyComputeTask(obj))

I hope this information proves helpful to you. Best of luck with your project.

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

Requesting data asynchronously using AJAX and displaying the filtered results on a webpage using JSON

When I send a request to my node.js server for a .json file containing person information (first name, last name), I want the data to be filtered based on user input. For example: When I request the .json file from the server, it gives me a list of people ...

Utilizing jQuery to execute functions from various files simultaneously with a single load statement

My goal is to achieve a basic include with jQuery, which involves loading functions from multiple files when the DOM is ready. However, this task proved to be more complex than anticipated: index.html <script type="text/javascript" src="res/scripts.js ...

In JavaScript, the checkboxes in all columns of a table with over 200 rows can be set, but only the checkboxes in the rows

Seeking help to implement toggle buttons for checkboxes on a page with a large table fetched from an external system. The table can have over 200 rows or more. Currently, I am facing an issue where I can only access and manipulate the visible checkboxes o ...

Ways to convert JavaScript object to hashmap

I am attempting to generate a map of type <String, Array()> from a JSON object. Suppose I have the following JSON structure: [ { "userId": "123123", "password": "fafafa", "age": "21" }, { "userId": "321321 ...

When hovering over an element, I must utilize a selector and reference a variable that was defined outside of the hover state in order to

Explaining this code may be a challenge, but I'll give it my best shot. Here's the code snippet: $('#navibar a').hover(function(){ var position = $(this).position(); var width = $(this).width(); $('#underliner'). ...

Is it possible to have a page transition when clicking a form button

I've been experimenting with this on my website Everything is functioning well, except when I add data-ftrans="slide" to a form button. For example: <form action"http://google.com"> <button data-ftrans="slide" type="submit" style="h ...

Need for utilizing a decorator when implementing an interface

I am interested in implementing a rule that mandates certain members of a typescript interface to have decorators in their implementation. Below is an example of the interface I have: export interface InjectComponentDef<TComponent> { // TODO: How ...

What is the process for sending a post request in Ionic 2 to a Node server running on localhost?

When working with Ionic, I utilized a service provider to access HTTP resources. The Service.ts file looks something like this. Here, data is represented as a JSON object. import { Injectable } from '@angular/core'; import { Http, Headers } fro ...

Breaking down objects or arrays to extract specific values in React components

Some articles recommend using ES6 destructuring for React props & state as a best practice. For example: const { showModal, hideModal } = this.props; While I understand the benefits of cleaner code, I recently discussed with another developer who suggest ...

Convert a prop into a data attribute using Vue.js 2

I have a basic component being displayed as <House :_people="[{'name': 'Kevin'}, {'name':'Bert'}, {'name': 'Timmy'}]"></House> The structure of the component is like this: <templ ...

Embarking on the journey of launching an Angular application on AWS CloudFront

I have a Laravel PHP application that functions as an API accessed by an Angular single-page app. Currently, the Angular app is situated within the public folder, but I aim to separate it so I can deploy it through Amazon CloudFront. I came across this ar ...

Update the content of a div on the WordPress homepage with the click of a button

Hey there, I'm currently working on customizing the Boutique theme for a website. My goal is to add two buttons to the home page that will display different sets of products when clicked. I've been attempting to use the visibility property, but h ...

Creating a visually appealing multi-bar chart in AngularJS: Tips for effectively presenting data

Imagine that I have the JSON data below: [ { month: "Jan", cost: 80, energy: 90 }, { month: "Feb", cost: 50, energy: 80 }, { month: ...

Troubleshooting issues with the add-comment and remove-comment functionalities in a react-redux application

I'm working on a comment widget using react-redux. I've encountered an issue where, when I click on the add comment button, nothing happens. Also, when I try to delete a comment, I get an error saying that map is not a function even though I am p ...

Displaying JSON data obtained from two separate arrays in AngularJS - is it possible?

I want to display the value of my Json element, "Baru20151","Lama20151","Baru20152","Lama20152", but I'm unsure how to do it. The element is fetched from 2 arrays, for the "Baru20151" elements, "20151" is obtained from the "isimasa" array in my Jso ...

Express.js and gridfs-stream are unable to retrieve the error

Imagine an effortless image download server where Express.js takes the request, fetches an image from MongoDB GridFS, and serves it as a response. Everything works fine when the request is valid and the file exists. The issue arises when it fails to catc ...

How can I combine multiple requests in RxJS, executing one request at a time in parallel, and receiving a single combined result?

For instance, assume I have 2 API services that return data in the form of Observables. function add(row) { let r = Math.ceil(Math.random() * 2000); let k = row + 1; return timer(r).pipe(mapTo(k)); } function multiple(row) { let r = Math.c ...

Unable to invoke a custom hook within another custom hook in a React application

I've developed a React application using create-react-app. Currently, I'm working on creating a custom hook that integrates with the Microsoft Authentication Library (MSAL). MSAL provides a custom React hook that I want to utilize within my own ...

The width of table cells expands even when using the box-sizing property set to border-box

Whenever I click the View button, I want to apply padding to the cells in my table. However, this action also increases the width of the cell. Despite researching various solutions like those found in this question, this one, and this post, I still encount ...

Angular Material datepicker designed for multiple input fields

In my form, I have multiple text box inputs where users enter dates. These fields are populated with values from a single instance of the Angular Material datepicker via TypeScript code. (dateChange)="onDateChange($event) When a user selects a date, such ...