Stream buffer values based on the given condition

I'm facing a challenge with processing a stream of strings where I need to emit each line individually. For example, starting with the string array:

let stream$ = from(['hello\n', 'world ', ' at\nhome\n and', ' more'])

I want to transform it into this stream:

'hello' 
 'world  at'
 'home'
 ' and more'

To accomplish this, I believe I need to use the merge operator after ensuring that there are no lines spanning multiple values in the stream. My initial approach looks like:

let break$ = new Subject()
stream$.pipe(
    flatMap(x => x.match(/[^\n]+\n?|\//g)),
    map(x => {
      if (x.endsWith('\n')) {
        break$.next(true)
        return x
      }
      return x
    })
    .buffer(break$)
)

However, the output of this pipe is currently a single array of values rather than grouping them by lines as expected:

[ 'hello\n', 'world ', ' at\n', 'home\n', ' and', ' more' ]

My desired outcome would be:

[
  ['hello\n'],
  ['world ', ' at\n'],
  ['home\n'],
  [' and', ' more'],
]

I do have a functioning solution available here, but it requires a subscription which I'd prefer to avoid in favor of lazy evaluation using a pipe.

Answer №1

To tackle this issue effectively, a viable strategy could involve leveraging the scanMap operator.

The scanMap operator functions by maintaining a state, akin to reduce, but triggers an event for each notification received from upstream. The necessity for retaining a state arises in scenarios such as receiving a string from upstream devoid of a \n, which necessitates storing the string in the state for subsequent use (primarily being attached ahead of the next notified string).

The provided code encapsulates the implementation of this method alongside explanatory comments:

import './style.css';

import { from, merge } from 'rxjs'; import { map, mergeMap, scan, share, last } from 'rxjs/operators';

let stream$ = from([
  'hello\n',
  'world ',
  ' at\nhome\n and',
  ' more',
  '\n and even more',
]);

let out$ = stream$.pipe(
  scan(
    (state, val) => {
      if (val.includes('\n')) {
        const prevTail = state.tail;
        state.tail = '';
        const lines = (prevTail + val)
          .split('\n')
          .filter((ls) => ls.length > 0);
        state.lines = lines;
        if (val[val.length - 1] !== '\n') {
          state.tail = lines[lines.length - 1];
          state.lines = lines.slice(0, lines.length - 1);
        }
        return state;
      }
      state.lines = [];
      state.tail = state.tail + val;
      return state;
    },
    { tail: '', lines: [] }
  ),
  share()
);

const outFirst$ = out$.pipe(
  map((obj) => obj.lines),
  mergeMap((ls) => ls)
);

const outLast$ = out$.pipe(
  last(),
  map((obj) => obj.tail)
);

merge(outFirst$, outLast$).subscribe({
  next: (x) => {
    console.log(x);
  },
});

For hands-on exploration, refer to the StackBlitz implementation.

The initial step involves utilizing the

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

Is it possible to utilize an npm package in TypeScript without a d.ts definition file?

Is it possible to use an npm package in TypeScript and Node.js without a .d.ts definition file? If so, how can I make it work? Currently, my code looks like this and I'm getting an error that says "cannot find module 'node-rest-client'" bec ...

injectIntl requires a component with props containing the `intl` attribute

I'm encountering an issue with the React.ComponentClass type in my project. The TypeScript version I'm using is 2.4.2- Here's the component code: import * as React from 'react'; import { injectIntl, InjectedIntlProps } from &apo ...

Creating a dynamic TypeScript signature that includes an optional argument

For some unknown reason, I am attempting to implement a reduce method on a subclass of Map: const nah = Symbol('not-an-arg'); class MapArray<A, B> extends Map<A, B> { reduce<T = [A, B]>(f: (prev: T, next: [A, B]) => any ...

Looking to transform a timestamp such as "2021-07-18T9:33:58.000Z" into a more readable format like 18th July for the date or 9:33 am for the time using Angular?

Is there a way to convert the Timestamp format "2021-07-18T9:33:58.000Z" to display as 18th July (for date) or 9:33 am (for time) in an Angular 11 application? Currently, my code looks like this: const myDate = new DatePipe('en-US').transform ...

Is it possible to concurrently hot module reload both the server (.NET Core) and client (Angular)?

Using the command 'dotnet watch run' to monitor changes in server code and 'ng build --watch' for Angular code updates has been successful. It rebuilds the code correctly into directories "bin/" and "wwwroot/" respectively. myapp.cspro ...

What is preventing the value from changing in auth.guard?

I am encountering an issue with the variable loggined, which I modify using the logTog() method. When I call this method, a request is made to a service where the current result is passed to auth.guard. However, in the console, it displays "undefined". Can ...

The package import path varies between dynamic code generation and static code generation

I have organized the src directory of my project in the following structure: . ├── config.ts ├── protos │ ├── index.proto │ ├── index.ts │ ├── share │ │ ├── topic.proto │ │ ├── topic_pb. ...

"Dealing with Angular .map() function returning an empty array or displaying error messages

I'm encountering two issues while attempting to display data from my API call using the following code... API Call: getProducts(id: number) { return from(Preferences.get({ key: 'TOKEN_KEY' })).pipe( switchMap(token => { ...

Can you apply transparency to a hex color variable in SCSS and then use that variable again?

In my app, I have a set of scss variables that represent colors used throughout the interface. For example: $primary-color: #00755E There are also colors that are variations of this primary color with different opacities. For instance, $primary-color with ...

Execute the "organizeImports" trigger through the terminal in TypeScript

One feature of VSCode is its editor capability to organize and clean imports in javascript and typescript files upon saving ( "source.organizeImports": true ). Inquiry Is there a way to trigger this action on a file using the command line? Something alo ...

Unable to transfer variable from a function to the test in Protractor

Currently, I am working on a test to verify the amount of gold in my possession. The test is being conducted using TypeScript and Protractor. Within this testing scenario, I have a method named GetAmountOfChips: public static GetAmountOfChips(): PromiseL ...

Can you inherit a type based on the keyof template in TypeScript?

I attempted a small hack to explore how DOM ts files map different element names to their types. My experiment involved trying to have a type MyType extend a different set of fields depending on the value of a string. Here is what I tried: interface Messa ...

What could be causing the CSS loader in webpack to malfunction?

I am currently working on implementing the toy example mentioned in the css-loader documentation which can be found at https://github.com/webpack-contrib/css-loader Additionally, I have also followed a basic guide that recommends similar steps: https://cs ...

Unable to load class; unsure of origin for class labeled as 'cached'

Working on an Angular 10 project in visual studio code, I've encountered a strange issue. In the /app/_model/ folder, I have classes 'a', 'b', and 'c'. When running the application in MS Edge, I noticed that only classes ...

How can JavaScript be used to parse an HTML string and convert it into tabular data in the form of a 2D array

My goal is to parse an HTML string client-side using React with TypeScript as our frontend framework. During the parsing process, I need to extract the styles associated with each element, whether it be inline styles, inherited styles, or styles defined wi ...

Jasmine is raising an error: "TypeError: Unable to access the property 'client' of an undefined object"

While running test cases for the EditFlag component in Angular, I encountered an error stating TypeError: Cannot read property 'client' of undefined. Additionally, I am looking to add a test case for a switch case function. Can someone assist me ...

Exploring the capabilities of argon2-browser in a cutting-edge setup with vite

After spending several hours attempting to implement the argon2-browser library in a Vue app with Vite, I have been encountering a persistent error. Despite following the documentation closely, I keep receiving the following message: This require call is ...

What exactly occurs when a "variable is declared but its value is never read" situation arises?

I encountered the same warning multiple times while implementing this particular pattern. function test() { let value: number = 0 // The warning occurs at this line: value is declared but its value is never read value = 2 return false } My curi ...

Issue encountered: Unable to access the property 'loadChildren' as it is undefined, while attempting to configure the path

How can I conditionally load the route path? I've attempted the code below, but it's throwing an error. Can someone guide me on how to accomplish this task? [ng] ERROR in Cannot read property 'loadChildren' of undefined [ng] i 「w ...

Issue with Angular @Input when navigating back in browser causing component to not render

Within the parent component, I am fetching a list of products from the store: // ... ngOnInit() { this.products$.subscribe(products => { this.products = products; }) } // ... <!-- ... --> <ng-container *ngIf="products"> ...