Rxjs get compose like behavior - javascript

I have multiple observables that I want to chain with the concat observable. But what I need is the same behavior like compose, b needs to get the results of a and c the results of b.
function a(observable) { return observable.map()... }
function b(observable) { return observable.map()... }
function c(observable) { return observable.map()... }
const obs = Observable.of([...])
Observable.concat(a(obs), b(obs), c(obs)).subscribe(...)
How can I do this?

As far as I know Rx does not have helper for composing function. However, is this what you was looking for?
function a(source$) {
return source$.map(x => x + '-first')
}
function b(source$) {
return source$.map(x => x + '-second')
}
function c(source$) {
return source$.map(x => x + '-third')
}
const composed = R.compose(c, b, a);
composed(Rx.Observable.interval(1000))
.subscribe(val => {
console.log(val);
});
<script src="https://unpkg.com/#reactivex/rxjs#5.0.3/dist/global/Rx.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>

Related

Need help solving this method chaining question?

I got asked this in an Interview and I couldn't solve it. Was wondering if any of you guys can help me.
fn("hello").fn("world").fn("!!!").fn();
function fn (str){
// Enter Solution Here
}
The solution should return 'hello world !!!'.
I tried method chaining and was able to get a partially right answer which is as follows:
function fn(str) {
var string = str;
this.fn1 = function(str1) {
string += " "+str1;
return this;
}
this.fn = function() {
console.log(string)
}
}
new fn("hello").fn1("world").fn1("!!!").fn();
but as you can see I cant get it to work unless I use fn1 as the function to concat the string. Any help will be appreciated, thanks.
Have the function return an object with one fn method. If, when you call it, it has an argument, update the string, otherwise return the string so you can log it.
function fn(str = '') {
return {
fn: function (s) {
if (s) {
str += ` ${s}`;
return this;
}
return str;
}
};
}
const output = fn('hello').fn('world').fn('!!!').fn();
console.log(output);
Additional documentation
Template/string literals
You could return an object with two properties, one for returning the complete string and another for collecting parts and retuning the object.
function fn(str) {
const
fns = {
fn: function () {
return str;
},
fn1: function (s) {
str += ' ' + s;
return fns;
}
};
return fns;
}
console.log(fn("hello").fn1("world").fn1("!!!").fn());
I think this should do the trick:
function fn(s){
return new function(){
this.str = s;
this.fn = (ns) => {if(ns){this.str += " "+ns; return this;} else return this.str;};
}
}
let a = fn("hello").fn("world").fn("!!!").fn();
console.log(a);
Seems like you need to use objects
const generic = {
"fn1":null,
"current":"",
"fn": () => {
//what do you want to do with "this.current"?
}
}
function fn(str) {
var ret = generic;
ret.fn1 = (wa) =>{
var again = generic;
again.current +=wa;
return again;
}
ret.current += str;
return ret;
}
You can return an object with a .fn() method which will
check if an argument is passed in or not to determine when to terminate the chain or continue chaining.
When no argument is sent, then it simply returns the accumulated string.
Otherwise, it calls fn() function again to accumulate to the string and get the next copy of the same structure as before:
const result = fn("hello").fn("world").fn("!!!").fn();
console.log(result);
function fn (str){
return {
fn(nextString) {
if (nextString === undefined)
return str;
return fn(`${str} ${nextString}`);
}
};
}
Since this operation is immutable, it means each link in the chain is independent, therefore it is no problem with assigning to variables to continue with different chains:
const helloWorld = fn("hello").fn("world");
const one = helloWorld.fn("one").fn();
const two = helloWorld.fn("two").fn();
const three = helloWorld.fn("three").fn();
console.log(one);
console.log(two);
console.log(three);
function fn (str){
return {
fn(nextString) {
if (nextString === undefined)
return str;
return fn(`${str} ${nextString}`);
}
};
}

Optional parameter in Arrow Function

I have a handler like
handleSelect = (k0, k1, v) => {
...
}
};
And I want to make k1 here optional. Is there a good way?
There is no good way. This isn't specific to React or arrows.
A variadic function with optional parameter in the middle requires to parse arguments:
handleSelect = (...args) => {
let k0, k1, v;
if (args.length > 2) {
[k0, k1, v0] = args;
} else {
[k0, v0] = args;
k1 = 'default';
}
...
};
This may result in obscure API. A better recipe for a function with several parameters some of which can be optional is to accept an object with options. A function doesn't depend on parameter order this way:
handleSelect = ({ k0, k1 = 'default', v }) => {
...
};

How can I determine what arguments have already been passed to a lodash partial function?

Let say I'm passing different data into a function in different places to work around some legacy decisions/code.
var partialFunction;
if(thisSituationIsCrazy(data, value)) {
partialFunction = _.partial(originalFunciton, data)
} else {
partialFunction = _.partialRight(originalFunction, value)
}
Is there a way to determine what values were actually passed in and at which place in the function? This is only for debugging purposes.
I'm hoping for something like partialFunction.arguments or some such that would shed some light on where it's gotten to, because the actual situation is more complicated than my example.
I've not seen a way to do it based on the lodash docs, nor on the source code. I've also not seen this mentioned in any blogs on the subject.
There is a concept of Higher order functions (HOF) in functional programming, which proves very powerful for this case.
HOF is a function that wraps another function, while it adds "extra hands" to the passed function.
//now a demo of what you want
function greet(a, b, c) {
return a + ' ' + b + ' ' + c;
}
function trackArguments(fn, ctx) {
const trackedArgs = [];
function tracker(partialFn, ...args) {
trackedArgs.push(...args);
const res = fn.call(ctx, partialFn, ...args);
res.trackedArgs = trackedArgs;
return res;
}
tracker.trackedArgs = trackedArgs;
return tracker
}
const trackedPartial = trackArguments(_.partial, _);
var sayHelloTo = trackedPartial(greet, 'hello', 'Stack');
console.log('Arguments passed already:', sayHelloTo.trackedArgs);
console.log('Execution works perfctly:', sayHelloTo('Overflow'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Obviously that is implemented inside the function, and when you invoke the final callback, its access the actual function from closer, and that is not exposed.
However if you want that really, you can go ahead and wrap that for your purpose:
//your wrapping inside a IIFE (Immediately-Invoked Function Expression)
(function() {
var _partial = _.partial,
_partialRight = _.partialRight;
_.partial = function() {
var args = Array.from(arguments).slice(1),
ret = _partial.apply(_, arguments);
ret.args = args;
return ret;
}
_.partialRight = function() {
var args = Array.from(arguments).slice(1),
ret = _partialRight.apply(_, arguments);
ret.args = args;
return ret;
}
})();
//now a demo of what you want
function greet(a, b, c) {
return a + ' ' + b + ' ' + c;
}
var sayHelloTo = _.partial(greet, 'hello', 'Stack');
console.log('Arguments passed already:', sayHelloTo.args);
console.log('Execution works perfctly:', sayHelloTo('Overflow'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

module.exports multiple functions in Jest testing

After reading the Jest documentation, when it's mentioned that to export a single function from a tested file they show the following example:
function sum(a, b) {
return a + b;
}
module.exports = sum;
Now, if I have multiple specific functions I want to export on my tested file, like this:
function sum(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
function subtract(a, b) {
return a - b;
}
module.exports = sum;
module.exports = multiply;
The multiply function is the only one being exported. How can I make these function be exported? Or only part of my file?
You can do something like this :
module.exports = {};
module.exports.sum = function sum(a, b) {
return a + b;
}
module.exports.multiply = function multiply(a, b) {
return a * b;
}
module.exports.subtract = function subtract(a, b) {
return a - b;
}
End you use it like this:
var MyMathModule = require('./my_math_module');
MyMathModule.sum(a, b);
MyMathModule.multiply(a, b);
MyMathModule.subtract(a, b);
First, in your example, all you are doing there is overriding the exports object with a function ( which is totally fine )
The exports and module.exports are an object and are actually the same object ( i.e. module.exports === exports // true )
To do what you want you can do this a couple ways:
exports.sum = sum
exports.multiply = multiply
or
module.exports = { sum: sum, multiply: multiply } // etc
or
module.exports.sum = sum
module.exports.multiply = multiply
Having in mind the answer to this question, i'll paste here 2 ways to do the same thing.
For example, you have the JS file called exercise5, like this:
//You can create an object with functions, as follows:
const wordAnalysis = {
type: (word) => typeof (word),
whiteSpaces: (word) => {
let wordAnalysis = word.includes(' ')
if (wordAnalysis) {
return 'It has spaces'
} else {
return "It doesn't has spaces"
}
}
}
//Or you can create several single functions, like the following:
function numberAnalysis(word) {
let isANumber = typeof (word) === 'number' ? true : false
return isANumber
}
// în order to avoid overwriting the module.exports, it is needed to do one of the following (I chose the first one):
// 1)
module.exports.firstPlace = wordAnalysis
module.exports.secondPlace = numberAnalysis
// 2)
// module.exports = {
// functions: functions,
// isANumber: isANumber
// }
// 3)
// exports.functions = functions
// exports.isANumber = isANumber
// 4)
// exports = {
// functions: functions,
// isANumber: isANumber
// }
Now the file test named exercise5.test.js:
const wordAnalysis = require('./exercise5')
const numberAnalysis = require('./exercise5')
test('It should give me the type of what was typed', () => {
expect(wordAnalysis.firstPlace.type('teste')).toEqual('string')
})
test('It should give me the type of what was typed', () => {
expect(wordAnalysis.firstPlace.type(22)).toEqual('number')
})
test("It should give true if what is typed has at least a space or false if it doesn't", () => {
expect(wordAnalysis.firstPlace.whiteSpaces('Jon is cool')).toEqual('It has spaces');
})
test("It should give true if what is typed has at least a space or false if it doesn't", () => {
expect(wordAnalysis.firstPlace.whiteSpaces('AllTogetherNow')).toBe("It doesn't has spaces");
})
test('it should analyse if the given expression is a number or not', () => {
expect(numberAnalysis.secondPlace(2)).toBeTruthy()
})
test('it should analyse if the given expression is a number or not', () => {
expect(numberAnalysis.secondPlace('jon')).toBeFalsy()
})
The only thing you need to be aware is to export/import the correct object/function, and of course call it when your are developing the test.

Multiple subscriptions to Observable

I create my own Observable and subscribed two functions to it. I would expect to have both functions executed for each element in the sequence but only the last one is.
let observer = null
const notificationArrayStream = Rx.Observable.create(function (obs) {
observer = obs;
return () => {}
})
function trigger(something) {
observer.next(something)
}
notificationArrayStream.subscribe((x) => console.log('a: ' + x))
notificationArrayStream.subscribe((x) => console.log('b: ' + x))
trigger('TEST')
Expected output
a: TEST
b: TEST
Actual output
b: TEST
Here's the JSBin: http://jsbin.com/cahoyey/edit?js,console
Why is that? How can I have multiple functions subscribed to a single Observable?
Subject
In your case, you could simply use a Subject. A subject allows you to share a single execution with multiple observers when using it as a proxy for a group of subscribers and a source.
In essence, here's your example using a subject:
const subject = new Subject();
function trigger(something) {
subject.next(something);
}
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
trigger('TEST');
Result:
a: TEST
b: TEST
Pitfall: Observers arriving too late
Note that the timing of when you subscribe and when you broadcast the data is relevant. If you send a broadcast before subscribing, you're not getting notified by this broadcast:
function trigger(something) {
subject.next(something);
}
trigger('TEST');
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
Result: (empty)
ReplaySubject & BehaviorSubject
If you want to ensure that even future subscribers get notified, you can use a ReplaySubject or a BehaviorSubject instead.
Here's an example using a ReplaySubject (with a cache-size of 5, meaning up to 5 values from the past will be remembered, as opposed to a BehaviorSubject which can remember only the last value):
const subject = new ReplaySubject(5); // buffer size is 5
function trigger(something) {
subject.next(something);
}
trigger('TEST');
subject.subscribe((x) => console.log('a: ' + x));
subject.subscribe((x) => console.log('b: ' + x));
Result:
a: TEST
b: TEST
To have multiple functions subscribe to a single Observable, just subscribe them to that observable, it is that simple. And actually that's what you did.
BUT your code does not work because after notificationArrayStream.subscribe((x) => console.log('b: ' + x)) is executed, observer is (x) => console.log('b: ' + x)), so observer.next will give you b: TEST.
So basically it is your observable creation which is wrong. In create you passed an observer as parameter so you can pass it values. Those values you need to generate somehow through your own logic, but as you can see your logic here is erroneous. I would recommend you use a subject if you want to push values to the observer.
Something like:
const notificationArrayStream = Rx.Observable.create(function (obs) {
mySubject.subscribe(obs);
return () => {}
})
function trigger(something) {
mySubject.next(something)
}
Every time you subscribe, you are overriding the var observer.
The trigger function only reference this one var, hence no surprise there is only one log.
If we make the var an array it works as intended: JS Bin
let obs = [];
let foo = Rx.Observable.create(function (observer) {
obs.push(observer);
});
function trigger(sth){
// console.log('trigger fn');
obs.forEach(ob => ob.next(sth));
}
foo.subscribe(function (x) {
console.log(`a:${x}`);
});
foo.subscribe(function (y) {
console.log(`b:${y}`);
});
trigger(1);
trigger(2);
trigger(3);
trigger(4);
A cleaner solution would be to use Subject, as suggested above.
Observables are not multicasting; unless you use any kind of Subject. You can of course create Subject, pipe the Observable output into like other answers propose.
However if you already have an Observalbe, it is way more convenient to use share() that turns Observable into Subject or shareReplay(n) which would be equivalent for ReplaySubject(n):
import {share} from 'rxjs/operators';
let observer = null
const notificationArrayStream = new Observable(obs => {
observer = obs;
}).pipe(share());
function trigger(something) {
observer.next(something)
}
notificationArrayStream.subscribe((x) => console.log('a: ' + x))
notificationArrayStream.subscribe((x) => console.log('b: ' + x))
trigger('TEST')
That's pretty much it.
You can build wrapper class Subscribable<> based on ReplaySubject. It would be cleaner than managing Subject and Observable:
export class Subscribable<T> {
private valueSource: Subject = new ReplaySubject(1);
public value: Observable;
private _value: T;
constructor() {
this.value = this.valueSource.asObservable();
}
public set(val: T) {
this.valueSource.next(val);
this._value = val;
}
public get(): T {
return this._value;
}
}
Usage:
let arrayStream : Subscribable<TYPE> = new Subscribable<TYPE>();
…
public setArrayStream (value: TYPE) {
this.set(value);
}
Handle value change:
arrayStream.value.subscribe(res => { /*handle it*/ });
Original article: http://devinstance.net/articles/20170921/rxjs-subscribable
Instead of using a Subject, it is also possible to use the publishReplay() + refCount() combo to allow an observable to multicast to multiple subscribers:
const notificationArrayStream = Rx.Observable.create(function (obs) {
observer = obs;
return () => {}
}).pipe(publishReplay(), refCount())
const subs = []
const ob = new Observable((s) => {
console.log('called')
subs.push(s)
})
const trigger = (v) => {
subs.forEach((sub) => {
sub.next(v)
})
}
ob.subscribe((v) => {
console.log('ob1', v)
})
ob.subscribe((v) => {
console.log('ob2', v)
})
trigger(1)
Change your code into something like this, and it will work. The point here is that each subscription is updated through its corresponding subscriber, if you have multiple subscriptions, you have to notify multiple subscribers. And in your case, you just notified the last one.

Categories