Efficiently get Observable of an array BehaviorSubjects - rxjs

I have this field:
smartArray: BehaviorSubject<BehaviorSubject<object>[]>;
//Example
[{key1: 'value1', key2: value2},...];
I now want to get and subscribe to one of the properties:
getObeservablePropertyOfSmartArray(index, property){
return this.smartArray.pipe(
//...getelementAtIndex <---dont know how
//lets say the elementatIndex is elem:
elem.pipe(
//get the property and return the Observable
)).asObservable();
}
So I basically need the equivalent of array[index] and object[key] and return Obeservable for BehaviorSubjects.
How can I do that?

you can use mergeMap to switch to that observable on the index, then you can use map to get the property value that you want
I made example for you here: https://stackblitz.com/edit/angular-qzeuzt

Related

subscribing to multiple observable while keeping reference to function parameters

I have an input of a string array for Enums I want to recieve from server:
enumList = ['somethin','branch','country', 'serviceType', 'thatandthis'];
I then have a generic http-service method that takes an enumList string as a parameter and returns an HttpClient observable for that Enum service:
this.webApi.getEnumByName('somethin','en').subscribe((res)=>{/*do something*/})
this.webApi.getEnumByName('branch','en').subscribe((res)=>{/*do something*/})...
I'm than combining the two into a loop
for (const item of this.enumList) {
this.webApi.getEnumByName(item).subscribe((res: any) => {
this.enums[item] = res;
});
}
But this is not good...
I want the a subscription that completes only once when all subscriptions has resolved, while keeping a reference to the associated item string
using an array of observables returned from this.webApi.getEnumByName(item), concat or forkJoin won't work because they won't keep refference to the associated string/key/token of the response e.g the string in the enumList.
The end result of these concatinated observables should be:
{
'somethin':{respopnse...},
'branch':{respopnse...},
'country':{respopnse...},
'serviceType':{respopnse...},
'thatandthis':{respopnse...}
}
breaking my head on this will appriciate an rxjs solution
If I understand right your problem, you may want to consider something like this.
First of all you build an Observable with a function like this
function obsFromItem(item) {
return this.webApi.getEnumByName(item).pipe(
tap(res => this.enums[item] = res),
)
}
The above logic says that as soon as getEnumByName notifies its result, than the result is set into this.enums at the right item.
Now that you have a similar function, you can create an array of Observables to be passed into forkJoin like this
arrayOfObs = enumList.map(item => obsFromItem(item))
forkJoin(arrayOfObs).subscribe()
When forkJoin(arrayOfObs) notifies, it means that all the Observables built via obsFromItem have emitted and therefore this.enums should be rightly filled.
forkJoin gives you parallel execution. If you substitute forkJoin with concat you get sequential execution.
In this article you may find some typical patterns of use of Obaservables with http calls.
You can combine several observables together like that:
forkJoin(enumList.reduce<any>((result, key) => {
result[key] = this.webApi.getEnumByName(key,'en');
return result;
}, {})).subscribe(allTogether => {
// allTogether.somethin;
// allTogether.branch;
// ...
});
You can create a function to pass in this.enumList and still getting the same reference
function getResponse(enum){
return forkJoin(....).subscribe(....)
}
or
forkjoin this.enumList with http call list
forkJoin(of(this.enumList), forkJoion(httpcall1,htttpcall2))
.subscribe([enum,responsesArray]=>....)

RxJS observable which emits both previous and current value starting from first emission

I have a BehaviorSubject which emits JavaScript objects periodically. I want to construct another observable which will emit both previous and current values of the underlying observable in order to compare two objects and determine the delta.
The pairwise() or bufferCount(2, 1) operators are looking like a good fit, but they start emitting only after buffer is filled, but I require this observable to start emitting from the first event of the underlying observable.
subject.someBufferingOperator()
.subscribe([previousValue, currentValue] => {
/** Do something */
})
;
On first emission the previousValue could be just null.
Is there some built-in operators that I can use to achieve the desired result?
Actually, it was as easy as pairing pairwise() with startWith() operators:
subject
.startWith(null) // emitting first empty value to fill-in the buffer
.pairwise()
.subscribe([previousValue, currentValue] => {
if (null === previousValue) {
console.log('Probably first emission...');
}
})
;
Here's a simple operator:
function withPreviousItem<T>(): OperatorFunction<
T,
{
previous?: T;
current: T;
}
> {
return pipe(
startWith(undefined),
pairwise(),
map(([previous, current]) => ({
previous,
current: current!
}))
);
}
The nice thing about this is that the result has meaningful property names and correct types:
previous is T | undefined
current is T (not T | null)
Stackblitz example
Here's the snippet for rxjs 6+
subject
.pipe(
startWith(undefined),
pairwise()
)
.subscribe(([previousValue, currentValue]) => {
/** Do something */
});
The value in startWith() should be undefined because there is no value. Typically null is defined as "we have a value and this value is empty".
scan (RX equivalent of a reduce) is an option here:
subject
.scan((accumulator, currentValue) => {
const previousValue = ...accumulator.slice(-1);
return [previousValue, currentValue];
}, [null]) // emitting first empty value to fill-in the buffer
.subscribe(([previousValue, currentValue]) => {
// ...
});
This can be extended to a more general case when you want to look at more than two items.

RxJS: Removing fields from an object

Lets say I have an object with 4 fields like this:
obj = {a:"3", b:"7", c:"10", d:"123"}
and I need to 'narrow' it to an object with fewer fields, like that:
newObj = {a:"3", c:"10"}
I know this can be done by deleting the fields (ie. delete obj.b )
My question is, can this be done with RxJS? And if yes, how???
Thanks a lot!
You if you have an Observable, that emits the shape above, you can do something like this:
Rx.Observable.of({ a:"3", b:"7", c:"10", d:"123" })
.map(({ a, c }) => ({ a, c }))
In the mapping function I destructure the object, then create an object literal with the shorthand syntax. Mind that these are es6/7 features.
But you really don't need rxjs or Observables to do this:
const original = { a:"3", b:"7", c:"10", d:"123" }
const changed = { a: original.a, c: original.c }
If you want to remove specific fields from the object (or stream of objects), rather than keeping specific ones, then you can use the following operator:
const removeFieldsOp = (props)=>obs=>obs.pipe(map(({...o})=>
(props.forEach(prop=>delete o[prop]), o)));
of({ a:"3", b:"7", c:"10", d:"123" }).pipe(removeFieldsOp(['b','d']))
.subscribe(console.log);
//{ a: '3', c: '10' }

combineAll does not emit on empty array

JSBIN Sample
I have a changeable set of child components (POJO object) that each have its own state stream. Each time a user triggers addChild/removeChild/clearChildren, a new set of children state streams is emitted with #switchMap. So far so good! (And so amazed by RxJS!)
With Rx.Observable.from(arrayOfStateStreams).combineAll() I get a good result as long as the arrayOfStateStreams isn't an empty array.
Since this is a partial state that is combined(Latest) on a higher level, I need to get an empty array emitted or the global state tree will contain old state data that is no longer true!
I can emit some reserved token like ['EMPTY-ARRAY-PLACEHOLDER-TOKEN'], but that's just weird.
A better way would be to always append one last stream into the array so the last index can be considered trash. Still confusing code and state though.
Using [null] is not OK, since we could have a child state of 'null'.
Anyone who can solve this in a good way? Can't this be supported since there should be no other representation of an empty array after #combineAll?
Credits go to github user trxcllnt who provided the following answer:
combineAll won't emit unless the combined Observables emit at least
one value, but you could check to ensure the collection you're
combining is empty or not, and either combine or emit an empty Array:
var arrayOfStreamsStream = Rx.Observable
.of(
[], [
Rx.Observable.of('blah-1'), // component state.
Rx.Observable.of('blah-2'),
Rx.Observable.of('blah-3')
], [], [
Rx.Observable.of('foo-1'),
Rx.Observable.of('qux-2')
]
)
.switchMap(function onMap(coll) {
return coll.length === 0 ?
Observable.of(coll) :
Observable.combineLatest(...coll);
})
.subscribe(function onSubscribe(data) {
console.log('onSubscribe START')
console.dir(data)
console.log('onSubscribe END')
})
This has nothing to do with combineAll. The problem is that Observable.from results in nothing (an empty observable) when passed an empty array.
The only viable solution that I can think of if you have to get a result from an empty array is to return something else in that case.
Ann example to illustrate the problem and a possible solution.
var data = [1, 2, 3, 4, 5];
log('With data: ');
Rx.Observable.from(data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: 1
// data: 2
// data: 3
// data: 4
// data: 5
var data = [];
log('Without data: ');
var nullDataObject = { msg: 'my null data object' };
Rx.Observable.from(data.length == 0 ? [nullDataObject] : data)
.subscribe(function (d) { log('data: ' + d); });
// Prints:
// With data:
// data: [object Object]
Runnable example on jsfiddle.
When consuming this you simply filter away the object representing an empty array where appropriate.
a possible workaround is to just pipe it with startWith();
combineLatest(potentiallyEmptyArray).pipe(
startWith<any>([])
);
Note: Similar issues exist with combineLatest() (the static version) which can be solved using defaultIfEmpty() - which works, but it screws up the typing of the output.
// array of Observables
const animals: Observable<{ species: 'dog' | 'cat' }>[] = [];
// Type '{ species: "dog" | "cat"; }[]' is not assignable to type 'never[]'.
combineLatest(animals).pipe(defaultIfEmpty([]));
In TypeScript you need to either know the type of the object or use <any>[] which means you then lose typing completely.
If you have a concrete type you can use one of these:
defaultIfEmpty<Animal[]>([])
defaultIfEmpty([] as Animal[])
I often don't have a concrete type for the return value of an observable. So I came up with an operator:
export const emptyArrayIfEmpty = () => <T>(observable: Observable<T[]>) =>
observable.pipe(defaultIfEmpty([] as T[]));
Then I can add the following and get out an empty array if animals === [] without losing any typing information:
combineLatest(animals).pipe(emptyArrayIfEmpty());

How to remove from an List Object in c#

I have an Action method in my controller which returns a List Object
Public ActionResult GetCats(long Id,string strsortorder,string dltIds)
{
var Result=objrepo.GetCats(Id);//this method returns me List of Result
}
My array looks like this:
var Result=[{CatId:1015,CatName:Abc},{CatId:1016,CatName:Acd},
{CatId:1017,CatName:Adf},{CatId:1018,CatName:CDdf},{CatId:1019,CatName:asdas},
{CatId:1020,CatName:Abc},{CatId:1021,CatName:Abc},{CatId:1022,CatName:Abc},
{CatId:1023,CatName:Abc},{CatId:1024,CatName:Abc}]
What I want to do is:
Using two more parameters in my Action Method "strsortorder" and "dltIds"
that have a list of ids like this:
strsortorder="1021,1015,1016,1019,1022";
dltIds="1017,1018,1020";
From this the "Result" returned from my method , I want to remove the records which are in "dltids" and the remaining array should be sorted in the order which I have in "strsortorder";
In the end the new object should look like this:
var NewResult=[{CatId:1021,CatName:Abc},{CatId:1015,CatName:Abc},
{CatId:1016,CatName:Acd},{CatId:1019,CatName:asdas},{CatId:1022,CatName:Abc},
{CatId:1023,CatName:Abc},{CatId:1024,CatName:Abc}]
Can any one help me in acheiving this in linq or any other way?
I want to avoid any type of loop or froeach here for max extent, I know it can be done by looping but I want to avoid this since the result can sometimes contain large amounts of data.
I realized you can use an ArrayList instead of a Dictionary and it would be faster. I think Dictionary is clear how it works but here is the "better" implementation using array list:
var excludeList = dltIds.Split(",".ToCharArray());
ArrayList sortList = new ArrayList(strsortorder.Split(",".ToCharArray()));
var NewResult =
Result.Where(item => ! excludeList.Contains(item.CatId.ToString()))
.OrderBy(item => {
if (sortList.Contains(item.CatId.ToString()))
return sortList.IndexOf(item.CatId.ToString());
return sortList.Count;
});
Original answer below:
Public ActionResult GetCats(long Id,string strsortorder,string dltIds)
{
var Result=objrepo.GetCats(Id);//this method returns me List of Result
var excludeList = dltIds.Split(",".ToCharArray());
int orderCount = 0; // used in the closure creating the Dictionary below
var sortList = strsortorder.Split(",".ToCharArray())
.ToDictionary(x => x,x => orderCount++);
// filter
var NewResult =
Result.Where(item => ! excludeList.Contains(item.CatId.ToString()))
.OrderBy(item => {
if (sortList.ContainsKey(item.CatId.ToString()))
return sortList[item.CatId.ToString()];
return sortList.Count();
});
}
How this works:
First I create lists out of your comma separated exclude list using split.
This I create a dictionary with the key being the ordering ID and the value being an integer that goes up by one.
For the filtering I look to see if an item is in the exclude array before I continue processing the item.
I then do a sort on matching against the key and the dictionary and returning the value -- this will sort things in the order of the list since I incremented a counter when creating the values. If an item is not in the dictionary I return one more than the maximum value in the dictionary which must be the count of the items. (I could have used the current value of orderCount instead.)
Questions?

Resources