creating lettable rxjs "string.split" operator - rxjs

I am attempting to do some string manipulation in rjxs, and while I can accomplish it with the built in behaviors on the string class and the array class in Javascript, I'm wanting to use this as an exercise to learn even more about rxjs and understand a fluent code-flow better.
To that end, it's imperative to me that I discover a way to do it that can fit in a fluent solution, instead of a series of variable assignments like I see in most rxjs examples.
Essentially, here's the situation; I've got a string of text;
const example = `
key || value
key || value
key || value
value
value
value
key || key[key1] = value | key[key2] = value
key || value
`;
The first thing that I need to do is use string.split('\n') to create an array of strings, so that I can through each line and perform further operation.
example.string.split('\n') does give the desired results, but trying to send this into rxjs begins to get rather mixed yield. With the pipe method, I know that I send the results into rxjs as an Observable, but I'm having a really troubling time grasping how to truly treat it from there without excessive nesting into the map operator.
For example, if I do ...
of(example.string.split('\n')).pipe(
map(results => results.toString().split('||')),
map(results => ... ),
...
).subscribe();
I can start to get a semblance of what I'm looking for, but what I'd really like to do is ...
of(example).pipe(
split('\n'),
split('||'),
concatMap(results => ...)
).subscribe();
Reading the documentation on lettable operators, seen here, it looks like this should be a pretty easy thing to create. In theory, it should look like this in my mind;
const split = (separator: string) => <T>(source: Observable<T>) =>
new Observable(observer => {
source.subscribe({
next(x) { observer.next(x.toString().split(separator)); },
error(err) { observer.error(err); },
complete() { observer.complete(); }
})
});
So that should make the whole code obvious enough;
of(example).pipe(
split('\n')
).subscribe(result => console.log(`[n]::${result}`));
But this doesn't give me what I really expect. I expected to get an array of the lines, but if I output it, I get ...
[n]::, key || value, key || value, key || value, ,
value, value, , value, key || key[key1] = value |
key[key2] = value, key || value,
I'm really unclear what I'm doing wrong, here. Since it's hard to demonstrate rxjs in most of the code playgrounds like plunkr or jsfiddle, at least to my knowledge, I've prepared a playground environment to demonstrate my work on stackblitz, if it helps.
You'll find all of the pertinent code in the playground/index.ts file. I've done the best I can to abstract away the need to have any knowledge of angular, as I've painstakingly earmarked the sections that should be left alone to make it continue showing output on the right side. If you do not know angular, but can help with rxjs, you should be able to work without ever disturbing that setup.
STACKBLITZ PLAYGROUND

Your code is working fine, just the es6 template string ${} flattened your array into a string. If you console.dir or log the result, you will see a correct array retrieved.

Related

Is it bad to use a variable from outside the observable pipe within an operator?

Is using a variable from outside an observable within an operator considered a (significantly) bad practice?
createObservableExample1(parameter1: string, obs$: Observable<string>): Observable<string> {
return obs$.pipe(
map( x => {
const returnValue = `${parameter1}, ${x}`;
return returnValue;
})
);
}
I understand you can do something like this:
createObservableExample2(parameter1: string, obs$: Observable<string>): Observable<string> {
return combineLatest([
of(parameter1),
obs$
]).pipe(
map( (x, y) => {
const returnValue = `${x}, ${y}`;
return returnValue;
})
);
}
But is it worth it?
Does this just come down to accessing variables from outside the scope of anonymous function? Would this force the context of the enclosing method to exist for longer than it should? I remember a code tool I used to use for C# complaining about something similar to this. I have found somewhat related topics by searching for, "anonymous functions and closures", but as of yet, nothing really discussing the scenario explained above.
I ask because I have been creating some relatively complex observables that have enormous operator chains, and constantly adding the needed variables, using combineLatest and of, from the parent scope can make the code even harder to follow.
When I teach Reactive programming to neophytes, I try to make them grasp : Do not break the reactivity by having uneccessary side effects :
no input that from a state (for example using a class or instance property
no storing outside value.
There is none of these red flags in your example. Your function is pure & idempotent with both implementation, go with what ever you like and if possible be consistant within your code base !

How to use LOGICAL OR Operator on Observable Boolean in Angular/RxJS

I am trying to do the logical OR/AND operator on two observable Boolean values.
I looked around a little and found on some old questions that combineLatest could be used for that, but it seems unfortunately it is deprecated now, and I can't find any other way to accomplish the same task.
Just things which might or might not make what I seek easier.
I need a way to apply OR operator on them in such a way that the resulting variable is also an observable Boolean and not just a Boolean (although this might work for me too).
Let me know what function or reference I can use to accomplish my task.
combineLatest ain't deprecated, at least not the form taking an array as argument :
import { combineLatest, of, map } from 'rxjs'
combineLatest(
[
of(true),
of(false)
]
)
.pipe(
map(([bool1, bool2]) => bool1 || bool2)
)
.subscribe(console.log)

rxjs mapTo operator: evaluate the return value at run time

function c() {
return Math.random();
}
source$.pipe(
map(a => c())
).subscribe(v => console.log(v));
Say there's a simple code like above. What I tried was logging the value when the source stream emits something but obviously, the value I log has nothing to do with the value from the source stream. So it got me considering using mapTo operator like this:
function c() {
return Math.random();
}
source$.pipe(
mapTo(c())
).subscribe(v => console.log(v));
But as you may guess, the value is always the same. More accurately speaking, it stays as the first value which is not what I want.
So my point is, I want the evaluation to be executed each time the source emits a value which I don't use at the evaluation. I can get it working like the first code by using map operator but it just doesn't seem right to use map when I don't use the value from the source stream. Is it okay to use map like this? Or is there any workaround for this kind of matter using mapTo or something else? Any insight would be appreciated!
According to the official definition, mapTo emits the given constant value on the output Observable every time the source Observable emits a value.
Therefore the behavior you described is the expected one. The first evaluation from Math.random() is kept and emitted for every time.
There seems nothing wrong to use map here to get the random values as you expect.

What's the difference between map and pluck in RxJS?

I am trying to understand the difference between map and pluck transformational operators in RxJS.
Can anyone help me with this?
The docs say
Pluck : Like map, but meant only for picking one of the nested
properties of every emitted object.
Therefore, let's say you have
[{ name: 'Joe', age: 30, job: { title: 'Developer', language: 'JavaScript' },
{ name: 'Sarah', age: 35 }]
and you want a list of all job titles.
Using map would be kind of a pain (because of the nullability of job), but with 'pluck' you can write pluck('job', 'title') and it will traverse the tree looking for job.title - and won't fail if job is null.
Example taken from : https://www.learnrxjs.io/operators/transformation/pluck.html
https://jsfiddle.net/btroncone/n592m597/
As #mgm87 said, you can perform an operation with map.
On the opposite, pluck is just taking a value.
For example, with map you could do something like that:
this.http.get('...some api url to get a user...')
.map(response => response.json())
.map(user => user.age > 18 ? 'major': 'minor')
.do(isMajorOrMinor => console.log(isMajorOrMinor))
So you can manipulate your data down the chain even conditionally.
BUT, for me one of the big differences is that map is typed.
Which means if you have some data let say:
interface IUser {
name: string;
age: number;
dogs: IDog[];
}
And you receive at some point a user, from which you want to get his dogs:
user$
.map(user => user.dogs)
.do(dogs => ...) // here, you do NOT need to precise (dogs: IDog[]) because Typescript will make a type inference
And that's why I'm always using map even to just "pluck" some data.
Stop using pluck!
Pluck is now planned to be removed in RxJS v8.
Do you know what is the reason?
Because after the addition of the optional chaining operator in JS, it's essentially, just a weak version of Map.
So what's the difference between the two?
Both are used to "transform" the data that is going to be emitted.
Map can be used to... map an observable emission (like we do in JS with Array.prototype.map), while Pluck is used to select/pick a property to emit (without having to emit properties that we don't care for, hence improving the performance).
But even before the optional chaining operator, you could just map the properties instead of plucking them. The result & performance were/are about the same.
pluck('prop')
is just a shorthand for:
map(x => x.prop)
Well, then what was the reason behind the implementation of Pluck?
It was basically implemented to achieve path traversal safety, meaning you could try to pluck a nested property without getting the error (that Map would throw) if the property is not defined:
pluck('foo', 'bar', 'baz'); // no error if foo is not defined
map(x => x.foo.bar.baz) // error if foo is not defined
With optional chaining, this advantage doesn't exists anymore, since we can just do this:
map(x => x?.foo?.bar?.baz)
This is the main reason why the Pluck operator is going to be deprecated and removed in the future.
Another important reason is TS typing for pluck is quite complex and not as robust as the map operator.
Got that info in GitHub:
The commit of Pluck deprecation is almost one year old but I still don't see any warning of future deprecation in the docs, hence I am posting here since I think it's a good thing to know. I already stopped plucking for this reason.
Map can perform an operation on each emitted value.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map
https://www.learnrxjs.io/operators/transformation/map.html
// value from observable = 10
map(x => 10*x)
// value from map = 100
Pluck simply picks one of the nested properties of each emitted value.
http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-pluck
https://www.learnrxjs.io/operators/transformation/pluck.html
// value from observable = {p = 10, w = 100}
pluck('p')
// value from pluck = 10
They are very similar, but as I understand it, map works with an array whereas pluck takes the values from an object.
This is the place to go for specifics.

Search IList of KeyValuePairs for two keys

I am putting in a temporary fix to code in which we want to validate attributes on an item. These are "swatchImageUrl" and "swatchVariantAttribute". If either one of these is provided, the other must be provided. Where I will check this is on a dictionary of the values. So what I have in place is the following:
if((transformedValues.Any(t => t.Key.Equals("swatchImageUrl")) &&
!transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute"))) ||
(transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute")) &&
!transformedValues.Any(t => t.Key.Equals("swatchImageUrl"))))
{
// throw an error here
}
This feels clunky and possibly inefficient (transformedValues will possibly be a very large list and my understanding is .Any() will end up enumerating the whole list if there are none) but I cannot think of a nicer way of doing this. 'transformedItems' is an IList of string key value pairs (so I can't use .ContainsKey etc.)
Is there some nice neater way of doing this that I am missing? Any insight is much appreciated.
Just in case anyone else is having similar brainfreeze. The obvious way to do this in a better way is as ASh pointed out;
if(transformedValues.Any(t => t.Key.Equals("swatchImageUrl")) != transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute")))
{ /*...*/ }

Resources