Merge topics together in RxJS like zip

Is it possible to create an observable that combines two subjects in a unique way, different from the zip function?

The goal is to combine two subjects so that when both have emitted values, the latest of their values is emitted. Then, after both emit at least once more, the latest values are emitted again, and so on.

Visual representation:

1 ---------- 2 ----- 3 -- 4 ------------------- 5 ——

------- a ------------------- b --- c — d —-————

Desired output:

------- 1a ----------------- 4b ---————-5d —-

For example, if subject1 emits 5 times and then subject2 emits once, the first emitted value would be a pair of both subjects' initial emissions: (subj1-emit5, subj2-emit1).

Answer №1

If you want to accomplish your goal easily, consider using the `combineLatest()` function along with `take(1)` and then add the `repeat()` operator to resubscribe after each sequence:

import { Subject, combineLatest, take, repeat } from 'rxjs';

const s1$ = new Subject<number>();
const s2$ = new Subject<string>();

combineLatest([s1$, s2$]).pipe(
  take(1),
  repeat(),
).subscribe(console.log);

setTimeout(() => s1$.next(1), 0);
setTimeout(() => s2$.next('a'), 200);
setTimeout(() => s1$.next(2), 300);
setTimeout(() => s1$.next(3), 400);
setTimeout(() => s1$.next(4), 500);
setTimeout(() => s2$.next('b'), 600);
setTimeout(() => s2$.next('c'), 700);
setTimeout(() => s2$.next('d'), 800);
setTimeout(() => s1$.next(5), 900);

// Output: [1, 'a'], [4, 'b'], [5, 'd']

Check out the live demo here: https://stackblitz.com/edit/rxjs-4vg6lu?devtoolsheight=60&file=index.ts

Answer №2

To start creating an almostZip function, I would approach it like this (detailed comments are included within the code to explain the process)

function almostZip(o1$: Observable<any>, o2$: Observable<any>) {
  // A variable is set in the closure to maintain the state of the Observable
  // Specifically, it stores the values emitted by the 2 input Observables
  const valuesEmitted = [null, null]
  const _o1$ = o1$.pipe(
    // When o1$ emits, the emitted value is stored in the first position of the state variable
    tap(v => valuesEmitted[0] = v)
  )
  const _o2$ = o2$.pipe(
    // When o2$ emits, the emitted value is stored in the second position of the state variable
    tap(v => valuesEmitted[1] = v)
  )

  // This is the Observable returned by almostZip function
  return merge(_o1$, _o2$).pipe(
    // Filter out all notifications until both slots of the state var are not null
    // If both are not null, then we can notify the pair - as explained further down
    filter(() => {
      return !!valuesEmitted[0] && !!valuesEmitted[1]
    }),
    // Notify a copy of the state
    // Emitting a copy of the state is necessary to reset the state soon after
    map(() => [...valuesEmitted]),
    // Reset the state
    // After notifying a pair of values from each Observable, the state needs to be reset
    // to prepare for the next pair of values
    tap(() => {
      valuesEmitted[0] = null
      valuesEmitted[1] = null
    }),
  )
}

You can see a demonstration of how this functions on this stackblitz link

Answer №3

What if we use the withLatestFrom operator?

import './custom-style.css';

import { rx, of, tap, interval, withLatestFrom } from 'rxjs';
const stream1$ = interval(1000);
const stream2$ = interval(5000);

stream2$
  .pipe(
    withLatestFrom(stream1$),
    tap(([firstData, secondData]) => {
      console.log(`Data from second source (every 5 seconds): ${firstData}, Data from first source (every 1 second): ${secondData}`);
    })
  )
  .subscribe();

// Check the browser console for results.

Try it on StackBlitz

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

When the button is clicked, the JavaScript function is not being executed

I'm having a strange issue with my second button not working as expected. Despite appearing to be straightforward, the "Reset" button does not seem to be triggering the clear() function. Within the HTML code, I have two buttons set up to interact wit ...

CORS blocked in the live environment

error + whole page As a newcomer to JavaScript, I recently deployed my project. During development, everything was functioning well. However, I am now facing an issue with CORS when attempting to sign in (registration works without any problems, confirmin ...

Using Javascript, access the 'second child' element by referencing the 'first child' element within a shared 'parent element'

// Retrieve the child_2 element using core javascript let parent = document.querySelector('.child_1').closest('.parent'); let child_2 = parent.querySelector('.child_2'); How can I achieve the same functionality as the jQuery ...

Narrowing down the keys of an indexable type in TypeScript based on their corresponding value types

If I have a generic indexable type, how can I retrieve the types of its values in order to narrow down to specific keys? // Let's assume check is a function that only allows the property 'a' as a string and raises an error if 'b' ...

The issue of basic authentication failing to function on Internet Explorer and Chrome, yet operating successfully on Firefox

Here is how my authentication code looks: public class BasicAuthenticationMessageHandler : DelegatingHandler { private const string BasicAuthResponseHeader = "WWW-Authenticate"; private const string BasicAuthResponseHeaderValue = "Basi ...

What is the best way to extract a variable from a URL and assign it to an Ionic variable

I am currently developing a project in Ionic 3 that utilizes the admob plugin. I have set up two variables (Mybanner and Myinterstital) to store the admob code, and I would like to retrieve the content of these variables from an external URL like this: ht ...

Reveal or conceal elements with a touch of animation

I am in the process of building my own website and I've been working on implementing a button that can toggle the visibility of a specific div element. While the functionality is there, I really want to incorporate an animation to enhance it. I attem ...

Struggles with loading order in Knockout.JS

I've encountered an issue with loading my scripts properly for a page utilizing a knockout.js form. Upon page load, my viewmodel js file isn't immediately loaded, resulting in errors that cause the validation messages to flash and hidden divs to ...

Import HTML document into a Bootstrap Popup

Currently, I am working on creating a modal that loads content dynamically. Below is the JavaScript code I have been using for this task: function track(id,title) { $('#listenTrack').modal('show'); $('#listenTrack').f ...

I'm curious if there is an eslint rule specifically designed to identify and flag any unnecessary spaces between a block comment and the function or

Can anyone help me find a solution to prevent the following issue: /** * This is a comment */ function foo() { ... } I need it to be corrected and formatted like this: /** * This is a comment */ function foo() { ... } ...

Look for identical values within a nested array

My data consists of a nested array where each element has a property called name, which can only be either A or B. I need to compare all elements and determine if they are all either A or B. Here is an example of the input: [ { "arr": { "teach ...

What steps should I take to incorporate Google sign-in on my website and gather user information efficiently?

I am looking to add the Google sign-in button to my website. While I am knowledgeable in HTML, PHP and JavaScript are not my strong suits. My goal is to allow users to sign in with their Google account and securely store their information on my database th ...

Best practice for passing a variable argument in a JavaScript callback?

After searching the internet without success, I couldn't find the answer to my problem. The issue revolves around a function that I have: function ParentFunction (DataBase, Parameters) { for (k = 0; k < DataBase.length; k++){ var ...

Setting up a function in React to utilize an image stored in state

I have a function in my react component for image classification that retrieves the image from the img tag using document.getElementById: const img = document.getElementById('animal_image');. The image uploaded via the file input updates the sta ...

Utilizing relative URIs in Node.js request library

I've encountered an issue with my code where node.js is unable to resolve the url: const request = require('request') const teamURL = `/users/${user._id}/teams`; const req = request({ url: teamURL, json: true }, function(error, ...

Exploring the possibilities of combining colspan and ngFor in an Angular material 6 table

Angular Material is being utilized for table rendering in my project. Code: <ng-container matColumnDef="type"> <th mat-header-cell *matHeaderCellDef> Workout type </th> <td mat-cell *matCellDef="let workout"> {{workout.type} ...

Trouble with Vue 3 watch not persisting after page refresh

Attempting to create a Vue application that supports two languages, utilizing local storage and store to store the selected language. Initially, everything appears to be functioning correctly. After the user logs in, the default language is displayed in b ...

Error message: Unable to load image from local source in Vue application

I am currently working on creating a simple dice roll game in VueJs. However, I encountered an issue with using the dice images as it keeps giving me a 404 error. When I attempted to use require(), it stated that the function was not defined. After some t ...

When creating a dynamic page number using JavaScript during a print event, the height of an A4 page is not taken into

While creating my A4 invoice using HTML, CSS, and JS, everything appears correctly in the print preview. However, I am encountering an issue where the page number is not aligned properly and extra empty pages are generated automatically. Below is a snippe ...

JQuery not properly validating inputs with required attributes after creation

I am currently developing a contact form that includes an "alternative address" <div id='alt'> with toggleable visibility. Inside this div, there is a required <input> field. I encountered an issue where toggling #alt twice (thus hidi ...