RXJS: alternately combine elements of streams - rxjs

I'd like to alternately combine elements of multiple streams:
var print = console.log.bind(console);
var s1 = Rx.Observable.fromArray([1, 1, 5]);
var s2 = Rx.Observable.fromArray([2, 9]);
var s3 = Rx.Observable.fromArray([3, 4, 6, 7, 8]);
alternate(s1, s2, s3).subscribe(print); // 1, 2, 3, 1, 9, 4, 5, 6, 7, 8
How looks the function definition of alternate?

Use zip and concatMap when working on observables that were created from arrays (as in your example), or zip and flatMap when working on observables that are inherently asynchronous.
Rx.Observable
.zip(s1, s2, s3, function(x,y,z) { return [x,y,z]; })
.concatMap(function (list) { return Rx.Observable.from(list); })
.subscribe(print); // 1, 2, 3, 1, 9, 4
Notice that this doesn't proceed anymore once one of the source observables completes. That's because zip is strictly "balanced" and it waits until all the sources have a matching event. What you want is a somewhat loose version of zip when dealing with completed sources.

If there is a value (for example undefined) that is not emitted by the source observables, this solution works:
var concat = Rx.Observable.concat;
var repeat = Rx.Observable.repeat;
var zipArray = Rx.Observable.zipArray;
var fromArray = Rx.Observable.fromArray;
var print = console.log.bind(console);
var s1 = fromArray([1, 1, 5]);
var s2 = fromArray([2, 9]);
var s3 = fromArray([3, 4, 6, 7, 8]);
alternate(s1, s2, s3).subscribe(print);
function alternate() {
var sources = Array.slice(arguments).map(function(s) {
return concat(s, repeat(undefined))
});
return zipArray(sources)
.map(function(values) {
return values.filter(function(x) {
return x !== undefined;
});
}).takeWhile(function(values) {
return values.length > 0;
}).concatMap(function (list) { return fromArray(list); })
}
Same example in ES6:
const {concat, repeat, zipArray, fromArray} = Rx.Observable;
var print = console.log.bind(console);
var s1 = fromArray([1, 1, 5]);
var s2 = fromArray([2, 9]);
var s3 = fromArray([3, 4, 6, 7, 8]);
alternate(s1, s2, s3).subscribe(print);
function alternate(...sources) {
return zipArray(sources.map( (s) => concat(s, repeat(undefined)) ))
.map((values) => values.filter( (x) => x !== undefined ))
.takeWhile( (values) => values.length > 0)
.concatMap( (list) => fromArray(list) )
}

Related

rxjs emmit values only after another observable completes

I have working pieace of code:
import { of } from 'rxjs';
import { map, takeWhile } from 'rxjs/operators';
const stream$ = of(3, 2, 1, 0, -1, -2, -3);
const ready$ = stream$.pipe(takeWhile((data) => data !== 0));
ready$.subscribe(console.log)
It will log 3, 2, 1.
How to create an observable that emmits all $stream values but only after 0 is emmited.
In other words: when ready$ completes, how to have observable that emmits values of stream$ except 3, 2, 1, 0?
You just have to replace takeWhile with skipWhile and add an extra skip(1) to ignore 0
const ready$ = stream$.pipe(skipWhile((data) => data !== 0),skip(1));

find Missing Range in a given a range

We need to find the missing range when main range is given and all sub ranges are given.
main range :[-10, 10]
sub Ranges: [-10, -5] , [-4, -3], [-2, 3], [7, 10]
Assumptions:
1) Range values can go upto 2^63.
2)sub ranges wont overlap and their order can be different.
for ex: the can be [-10, -5],[7, 10], [-2, 3], [-4, -3]
what is best algorithm to find the missing range here?
Assuming the intervals are unsorted, I do not see avoiding a sorting cost since each interval can be a singleton ([n,n]). That cost can be O(n log n) for a comparison sort or O(n) for a radix sort. From now on, let's assume that input intervals are sorted and contain no overlaps. Here is a O(n) single pass Python implementation:
xs = [[-10, -5] , [-4, -3], [-2, 3], [7, 10]]
bounds = (-10, 10)
missing = list()
# pre-processing
xs_sorted = sorted(xs)
# pre-processing a missing range on the lower bound
if bounds[0] < xs_sorted[0][0]:
missing.append((bounds[0], xs_sorted[0][0]-1))
def f_reduce(a, b):
if a[1] + 1 == b[0]:
# merge contiguous intervals
return (a[0], b[1])
else:
# gap detected; add the gap to the missing range list
# and move to the next value
missing.append((a[1]+1, b[0]-1))
return b
from functools import reduce
reduce(f_reduce, xs_sorted)
# post-processing on a missing range on the upper bound
if bounds[1] > xs_sorted[-1][1]:
missing.append((xs_sorted[-1][1]+1, bounds[1]))
print(missing)
# [(4, 6)]
The approach is to use a functional style reduce with a stinky side-effect. When the function f_reduce encounters two intervals (a, b) and (c, d), we return a compound interval (a, d) if b + 1 == c. Otherwise, a gap is detected and stored; the returned interval is (c, d). The pre and post processing steps are dealing with nuisance cases when gaps occur on the two extreme ranges of the interval.
Try using the following way, it works in O(n) where n is the range width.
// entire range is initially 0.
int arr[range_max - range_min + 2] = {0};
//for each sub_range increment the values by 1.
for(int i = 0; i<n; i++){
arr[sub_range_min[i] - range_min] += 1;
arr[sub_range_min[i] - range_max + 1] -= 1;
}
for(int i = 1; i< range_max - range_min + 2; i++){
arr[i] += arr[i-1];
}
// all the uncovered area in the range by the sub_ranges will be marked 0 in the array.
Looks like you can pass through array and find index i where
xi != y(i-1)
Pair
(y(i-1), xi)
is the answer
Assuming only one missing interval:
We may do it in O(k) where k is the number of subranges.
Make a chain (like Chasles) of the connected subranges (since they trivially do not overlap).
At the end, three possible cases:
only one chain: the missing subrange is at the beginning
or at the end
two chains: it is in between
At the current subinterval, check if it is a prolongement of a chain.
if not create another chain.
if yes, increase the chain, like ssssnake. Then maybe it connects that chain with another one. Then reduce the two chains and the sub interval as a single big chain
A chain may simply be representated with its left and right
And to find the chain to increase, may simply use a hashmap on left and another one on right
function getMissingSub (subs, minRange, maxRange) {
const chainsByLeft = new Map () // left -> [left, whatsoever]
const chainsByRight = new Map () // right -> [whatsoever, right]
// before: [[a, [a, whatsoever]]]
// after: [[newVal, [newval, whatsoever]]]
function prolongeLeft (x, newVal) {
const chain = chainsByLeft.get(x)
const old = chain[0]
chain[0] = newVal
chainsByLeft.set(newVal, chain)
chainsByLeft.delete(old)
return chain
}
function prolongeRight (x, newVal) {
const chain = chainsByRight.get(x)
const old = chain[1]
chain[1] = newVal
chainsByRight.set(newVal, chain)
chainsByRight.delete(old)
return chain
}
subs.forEach(([a,b]) => {
if (chainsByLeft.has(b) || chainsByRight.has(a)) {
if (chainsByLeft.has(b)) {
// prolonge on the left
const chain = prolongeLeft(b, a)
if (chainsByRight.has(a) ) {
prolongeRight(a, chain[1])
}
} else {
const chain = prolongeRight(a, b)
if (chainsByLeft.has(b) ) {
prolongeLeft(b, chain[0])
}
}
} else {
// new chain
const chain = [a, b]
chainsByLeft.set(a, chain)
chainsByRight.set(b, chain)
}
})
let missingRange
if (chainsByLeft.size === 1) {
const [, [left, right]] = chainsByLeft.entries().next().value
if (left === minRange) {
missingRange = [right, maxRange]
} else {
missingRange = [minRange, left]
}
} else {
const [[, [l1, r1]], [, [l2, r2]]] = chainsByLeft.entries()
if (r1 < r2) {
missingRange = [r1, l2]
} else {
missingRange = [r2, l1]
}
}
return { missingRange, chainsByLeft }
}
const dump = ({ missingRange: [a,b] }) => console.log(`missing [${a}, ${b}]`)
dump(getMissingSub([[0, 1],[1, 2]], 0, 4))
dump(getMissingSub([[0, 1],[1, 2]], -1, 2))
dump(getMissingSub([[0, 1],[2, 3]], 0, 3))
dump(getMissingSub([[-10, -5] , [-4, -3], [-2, 3], [7, 10]], -10, 10))
If you have several missing ranges, obviously you can have more than two chains, then you may need a sort to order the chains and directly find the gap between consecutive chains
//COPY PASTED FROM BEFORE
function getMissingSub (subs, minRange, maxRange) {
const chainsByLeft = new Map () // left -> [left, whatsoever]
const chainsByRight = new Map () // right -> [whatsoever, right]
// before: [[a, [a, whatsoever]]]
// after: [[newVal, [newval, whatsoever]]]
function prolongeLeft (x, newVal) {
const chain = chainsByLeft.get(x)
const old = chain[0]
chain[0] = newVal
chainsByLeft.set(newVal, chain)
chainsByLeft.delete(old)
return chain
}
function prolongeRight (x, newVal) {
const chain = chainsByRight.get(x)
const old = chain[1]
chain[1] = newVal
chainsByRight.set(newVal, chain)
chainsByRight.delete(old)
return chain
}
subs.forEach(([a,b]) => {
if (chainsByLeft.has(b) || chainsByRight.has(a)) {
if (chainsByLeft.has(b)) {
// prolonge on the left
const chain = prolongeLeft(b, a)
if (chainsByRight.has(a) ) {
prolongeRight(a, chain[1])
}
} else {
const chain = prolongeRight(a, b)
if (chainsByLeft.has(b) ) {
prolongeLeft(b, chain[0])
}
}
} else {
// new chain
const chain = [a, b]
chainsByLeft.set(a, chain)
chainsByRight.set(b, chain)
}
})
let missingRange
if (chainsByLeft.size === 1) {
const [, [left, right]] = chainsByLeft.entries().next().value
if (left === minRange) {
missingRange = [right, maxRange]
} else {
missingRange = [minRange, left]
}
} else {
const [[, [l1, r1]], [, [l2, r2]]] = chainsByLeft.entries()
if (r1 < r2) {
missingRange = [r1, l2]
} else {
missingRange = [r2, l1]
}
}
return { missingRange, chainsByLeft }
}
//ENDCOYP PASTED
function getMissingSubs(subs, minRange, maxRange) {
const { missingRange, chainsByLeft } = getMissingSub.apply(null, arguments)
const missingRanges = []
;[[minRange, minRange], ...chainsByLeft.values(), [maxRange, maxRange]]
.sort((a,b) => a[0]-b[0]).reduce((chain, next) => {
if (chain[1] !== next[0]) {
missingRanges.push([chain[1], next[0]])
}
return next
})
return { missingRanges }
}
const dump2 = ({ missingRanges }) => console.log(`missing2 ${JSON.stringify(missingRanges)}`)
dump2(getMissingSubs([[0, 1],[1, 2]], 0, 4))
dump2(getMissingSubs([[0, 1],[1, 2]], -1, 2))
dump2(getMissingSubs([[0, 1],[2, 3]], 0, 3))
dump2(getMissingSubs([[-10, -5] , [-4, -3], [-2, 3], [7, 10]], -10, 10))

Is there an RX method which combines map and filter?

I'm new to RxJS. I know I could just .filter and .map an observable to get the change I'm looking for. But, is there any method which combines the two into one function?
Yes there is.
FlatMap.
Suppose you have an Observable of numbers (1, 2, 3, 4, 5, ...) and you want to filter for even numbers and map them to x*10.
var tenTimesEvenNumbers = numbers.flatMap(function (x) {
if (x % 2 === 0) {
return Rx.Observable.just(x * 10);
} else {
return Rx.Observable.empty();
}
});
As of rxjs v6.6.7, the solution becomes as following:
// Initialise observable with some numbers
const numbers = of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Pipe the observable using mergeMap
const tenTimesEvenNumbers = numbers.pipe(
mergeMap((x: number) => {
// If the number is even, return an observable containing the number multiplied by ten
// Otherwise return an empty observable
return x % 2 === 0 ? of(x * 10) : EMPTY;
})
);
// Subscribe to the observable and print the values
tenTimesEvenNumbers.subscribe((value: number) =>
console.log('Value:', value)
);
The above will print:
Value: 20
Value: 40
Value: 60
Value: 80
Value: 100
Here is a working stackblitz as well.

C# EF Linq bitwise question

Ok for example, I am using bitwise like such: Monday = 1, Tuesday = 2, Wednesday = 4, Thursday = 8 etc...
I am using an Entity Framework class of Business.
I am using a class and passing in a value like 7 (Monday, Tuesday, Wednesday).
I want to return records that match any of those days
public List<Business> GetBusinesses(long daysOfWeek)
{
using (var c = Context())
{
return c.Businesses.Where(???).ToList();
}
}
Any help would be appreciated. Thanks!
EDIT
Ok, so I am attempting the following:
var b = new List<Business>();
var b1 = new Business(){DaysOfWeek = 3};
b.Add(b1);
var b2 = new Business() { DaysOfWeek = 2 };
b.Add(b2);
var decomposedList = new[]{1};
var l = b.Where(o => decomposedList.Any(day => day == o.DaysOfWeek)).ToList();
But l returns 0 results assuming in the decomposedList(1) I am looking for monday.
I created b1 to contain Monday and Tuesday.
Use the bitwise and operator & to combine your desired flags with the actual flags in the database and then check for a non-zero result.
var b1 = new { DaysOfWeek = 3 };
var b2 = new { DaysOfWeek = 2 };
var b = new[] { b1, b2 };
var filter = 1;
var l = b.Where(o => (filter & o.DaysOfWeek) != 0);
foreach (var x in l)
{
Console.WriteLine(x);
}
If you have an array of filter values simply combined then with an OR | first:
var filterArray = new []{1, 4};
var filter = filterArray.Aggregate((x, y) => x | y);
You must decompose the long value(bitflagged enum will be better) to it's parts then pass it to Where
return c.Businesses.Where(o=> DecomposeDays(dayParam).Any(day => day==o)).ToList();
EDIT:
decompose method:
private static IEnumerable<byte> DecomposeDays(byte dayParam)
{
var daysOfWeek = new List<byte> { 1, 2, 4, 6, 8 ,16};
return daysOfWeek.Where(o => (o & dayParam) == dayParam);
}

Linq/lambda question about .Select (newby learning 3.0)

I am playing with the new stuff of C#3.0 and I have this code (mostly taken from MSDN) but I can only get true,false,true... and not the real value :
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var oddNumbers = numbers.Select(n => n % 2 == 1);
Console.WriteLine("Numbers < 5:");
foreach (var x in oddNumbers)
{
Console.WriteLine(x);
}
How can I fix that to show the list of integer?
Change your "Select" to a "Where"
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var oddNumbers = numbers.Where(n => n % 2 == 1);
Console.WriteLine("Odd Number:");
foreach (var x in oddNumbers)
{
Console.WriteLine(x);
}
The "Select" method is creating a new list of the lambda result for each element (true/false). The "Where" method is filtering based on the lambda.
In C#, you could also use this syntax, which you may find clearer:
var oddNumbers = from n in numbers
where n % 2 == 1
select n;
which the compiler translates to:
var oddNumbers = numbers.Where(n => n % 2 == 1).Select(n => n);
numbers.Select(n => n % 2 == 1);
Change this to
numbers.Where(n => n % 2 == 1);
What select does is "convert" one thing to another. So in this case, it's "Converting" n to "n % 2 == 1" (which is a boolean) - hence you get all the true and falses.
It's usually used for getting properties on things. For example if you had a list of Person objects, and you wanted to get their names, you'd do
var listOfNames = listOfPeople.Select( p => p.Name );
You can think of this like so:
Convert the list of people into a list of strings, using the following method: ( p => p.Name)
To "select" (in the "filtering" sense of the word) a subset of a collection, you need to use Where.
Thanks Microsoft for the terrible naming

Resources