Convert array to sequence of items with flatMap - rxjs

In RxJS I want to convert an array that I have at some point into a sequence of items that are in the array. I found two ways to do it: Option 1 & 2, which I guess, do the same thing:
const obj = { array: [1, 2, 3, 4, 5] };
const observable = Observable.of(obj);
// Option 1
observable.flatMap(x => {
return Observable.from(x.array);
}).subscribe(console.log);
// Option 2
observable.flatMap(x => x.array).subscribe(console.log);
// Option 3 ?
Are there nicer / better ways that express what I'm doing , I mean without flatMap operator?

I think you've pretty much reached the shortest possible way. The only improvement I might suggest is avoid using callback functions at all:
const obj = { array: [1, 2, 3, 4, 5] };
const observable = Observable.of(obj);
observable
.pluck('array')
.concatAll() // or mergeAll()
.subscribe(console.log);
See live demo: https://jsbin.com/zosonil/edit?js,console

Related

rxjs `toArray` is not working as expected

I tried to de-duplicate the sent arrays and then merge them into arrays
import { from, BehaviorSubject, distinct, switchMap, toArray } from "rxjs";
let list$ = new BehaviorSubject([1, 2, 3, 2, 3, 5]);
list$.pipe(
switchMap((e) => from(e)),
distinct(),
toArray()
).subscribe(console.log);
expected result:
BehaviorSubject -> [1, 2, 3, 2, 3, 5]
switchMap -> 1 2 3 2 3 5
distinct -> 1 2 3 5
toArray -> [1, 2, 3, 5]
console.log -> [1, 2, 3, 5]
Actually did not receive any value in console.log, why is this and how can I work as expected
"rxjs": "^7.2.0"
toArray only emits once the source observable completes.
The following should work as expected.
list$.pipe(
take(1),
switchMap(e => e),
distinct(),
toArray()
).subscribe(console.log);
If what you really want to do is filter unique values of an array, then RxJS's unique operator might be overkill. I wouldn't bother turning your array into a stream. Just filter the array.
list$.pipe(
map(a => [...new Set(a)])
).subscribe(console.log);
So, if the source does not stop after the first notification, I assume that it will continue emit other arrays and that you want to filter the duplicates on each array emitted. In other words, if the list$ of your example emits first [1, 2, 3, 2, 3, 5] and then [3, 2, 1, 6, 6, 6,] what you want to log are 2 arrays, [1, 2, 3, 5] and [3, 2, 1, 6].
If my assumption is right, than the solution could be the following
list$.pipe(
concatMap((e) => from(e).pipe(
distinct(),
toArray()
)),
).subscribe(console.log);
The trick here is that each from(e) stream will complete when there are no more elements in the array. Therefore, since it completes, the toArray operator can actually work.
scan could do the trick.
list$.pipe(
switchMap((e) => from(e)),
distinct(),
scan((acc, curr) => [...acc, curr], []),
).subscribe(console.log);
// will print: [1], [1, 2], [1, 2, 3], [1, 2, 3, 5]
You could insert debounceTime in the pipe, if you need less emissions:
list$.pipe(
switchMap((e) => from(e)),
distinct(),
scan((acc, curr) => [...acc, curr], []),
debounceTime(0)
).subscribe(console.log); // will print [1, 2, 3, 5]
If the only requirement is to remove duplicates, you're better off handling it using vaniall JS. See here: https://stackoverflow.com/a/9229821/6513921
We'll take the shortest solution without any regards to performance: uniq = [...new Set(array)];
You could then write a custom RxJS operator to include it in the pipe with other operators.
const { BehaviorSubject, from } = rxjs;
const { map, switchMap } = rxjs.operators;
const uniqueArray = (obs$) => {
return (obs$) => {
return obs$.pipe(
map(arr => [...new Set(arr)])
);
};
};
const sub = new BehaviorSubject([1, 2, 3, 2, 3, 5]);
sub.asObservable().pipe(
uniqueArray()
).subscribe(console.log);
sub.next([6, 3, 1, 6, 7, 1, 1]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#6.2.2/bundles/rxjs.umd.min.js"></script>

Any way to reset Interval value if a certain condition met?

Suppose I have an array of integers and I want to iterate over this array in a interval of one second, the current second being the index of the array. Once the index reaches the end, I want to reset the interval value. Whats the right approach to achieve this behavior?
Stackblitz example
Code:
const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
interval(1000)
.pipe(
tap(second => {
if (array[second]) {
console.log(array[second]);
} else {
//reset interval to run in a cyclic manner?
}
})
).subscribe();
So you want to repeat the same sequence indefinatelly.
import { from, of } from "rxjs";
import { concatMap, repeat, delay } from "rxjs/operators";
const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
from(array)
.pipe(
concatMap(second => of(second).pipe(
delay(second * 1000),
)),
repeat(),
)
.subscribe(console.log);
Live demo: https://stackblitz.com/edit/rxjs-interval-pmlj6e

Rxjs - emit if the pair appears

Question about rxjs puzzle.
I have a random numbers stream.
the number takes [0-9] numbers. so the stream looks like:
0, 4, 2, 8, 9, 0, 3, 9, ...
I want to emit true if number 9 after 5. In other words, if the pair [5, 9] appears in the stream.
what the pipe should be?
Example code is here:
import { interval } from "rxjs";
import { map } from "rxjs/operators";
const numbers =
interval(500).pipe(map(() => Math.floor(Math.random() * 10))); // it takes [0-9] numbers
numbers.pipe(
// what the pipe should be?
).subscribe(console.log);
You could use the pairwise() operator:
src$.pipe(
pairwise(),
map(([prev, crt]) => prev === 5 && crt === 9)
)

Find vector of unique elements and vector of repeats using for loops and if conditions

I want a program that takes a vector of integers and creates two new vectors, one which contains each unique element, and another which contains the number of times these unique elements were repeated in the original vector.
So,
[1, 2, 2, 3, 3, 3]
has unique elements:
[1, 2, 3]
with the following number of repeats:
[1, 2, 3]
I know this has been asked before, but I want to achieve this using purely for loops and if conditions if need be. For the same reason, the code can be given in pseudocode obviously.
Please be nice, I am literally a beginner who learned programming syntax a few days ago.
Here a solution
var arr = [1, 2, 2, 2, 3, 3, 3, 3]
var unique = [...new Set(arr)]
var repeated = []
var i = 0
unique.map((a) => {
arr.map((b) => {
if(a === b) i++
})
repeated.push(i)
i = 0
})
console.log("[" + arr.join(",") + "] has unique elements [" + unique.join(",") + "] with the following number of repeats [" + repeated.join(",") + "]")

Wrong type with sort in Typescript

var cheapest = leaves.sort((a,b) => <boolean>(<number>a.cost < <number>b.cost));
//also tried without casting
Gives me the following error:
'Error'
message: 'Argument of type '(a: any, b: any) => boolean' is not assignable to parameter of type '(a: any, b: any) => number'.
Type 'boolean' is not assignable to type 'number'.'
How should i fix this?
Edit:
The js code ( original) is taken from :
https://github.com/atomicptr/goap/blob/gh-pages/gameplay/ai/planner.js , which indeed seems to sort by bool instead of number.
This is not how Array.sort works. You need to return a number, but the predicate you've given returns a boolean (The less than (<) operator results in true or false). The sort order is determined by whether the number your function returns is negative, positive, or zero. The MDN example illustrates this well with an example compare function.
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
If you want to sort ascending you can do this with
var cheapest = leaves.sort((a,b) => a.cost - b.cost);
assuming that leaves is typed correctly so a and b have their types correctly inferred.
The comparator function of a sort should return -1, 0, or 1. Returning a boolean instead (effectively 1 or 0) will fail to sort the list correctly!
For example, in Chrome (sort implementations are host-dependent), this line:
[1, 2, 5, 6, 5, 5, 4, 3, 2, 1, 4, 2, 4, 6, 3].sort(function(a, b) { return a < b; })
Produces:
[3, 3, 5, 6, 5, 5, 4, 6, 4, 4, 2, 2, 2, 1, 1]
Which is not sorted!
You should write something like a > b ? 1 : a === b ? 0 : -1 instead
When using Array.prototype.sort(), a comparator function can be used to properly define sorting behaviour. According to the MDN Javascript documentation:
To compare numbers instead of strings, the compare function can simply subtract b from a.
Thus to sort the array 'leaves' in ascending order:
var cheapest = leaves.sort((a,b) => a.cost- b.cost);
With a Typescript generic:
var cheapest = leaves.sort((a,b) => <number>(a.cost- b.cost));

Resources