split observable array into two observable arrays - rxjs

I have an api call which returns data with Observable<User[]> (where user is
{id:string, status:string}
what I would like to do is to split the observable into two different observables, based on the status (active/inactive)
I googled and Stackoverflowed, but the only samples I could find only showed single-value arrays [1,2,3,4] etc
I tried to apply the same technique to the array of objects
const foo = this.userApi
.find()
.pipe(partition(item => item.status === 'active'));
hoping that this would return foo[0] holding the active and foo[1] holding the inactive users
However, the vsCode editor complains about
[ts]
Argument of type 'UnaryFunction<Observable<User[]>, [Observable<User[]>, Observable<User[]>]>' is not assignable to parameter of type 'OperatorFunction<any, any>'.
Type '[Observable<User[]>, Observable<User[]>]' is not assignable to type 'Observable<any>'.
Property '_isScalar' is missing in type '[Observable<User[]>, Observable<User[]>]'.
[ts] Property 'status' does not exist on type 'User[]'.
any
which implies that the item is an array ...
I also tried groupBy and hit the same problem
const foo = this.userApi
.find<User>()
.pipe(groupBy(item => item.status));
[ts] Property 'status' does not exist on type 'User[]'
as you can probably tell, I'm no rxjs expert and have hit a wall at this point, and would appreciate any pointers

You had in my opinion the correct idea. Using partition seems like it is the right way to do it. But partition is really weird for now (and soon deprecated for something better: splitBy see here for more info: https://github.com/ReactiveX/rxjs/issues/3807).
Anyway here's how to use partition:
const [activeUsers$, inactiveUsers$]: [Observable<User>, Observable<User>] =
partition(
(x: User) => x.status === 'active'
)(from(mockUsers));
Stackblitz example here: https://stackblitz.com/edit/typescript-ziafpr
See also https://github.com/ReactiveX/rxjs/issues/2995

You are not returning the item but instead the entire array to groupBy. Try this
const foo = this.userApi
.find<User>()
.pipe(mergeMap(users=>from(users),
groupBy(item => item.status));

Related

DB::get is Array in Laravel but it says it is not array

I thought the data which is from DB::get() is Array.
However , the console says it is not array.
$fruitList = Food::where('id' => 300)->get(['id']);
shuffle($fruitList);
ErrorException: shuffle() expects parameter 1 to be array, object given in file
The return value of get() is not an array. it's Laravel array collection you can convert it to an array or use shuffle of array collection:
$fruitList = Food::where('id' => 300)->get(['id'])->toArray();
shuffle($fruitList);
with array collection:
$fruitList = Food::where('id' => 300)->get(['id'])->shuffle();
Just like #A.seddighi mentioned, using get() or all() gives you a collection. It may seem like an array when you output it using return or print but it is different.
Collections can be filtered, queried and so on. e.g
$fruitList->has('price')
etc.
To get an array simply called the toArray() method on it, you may also use flatMap(), mapWithKeys() etc. Make sure you follow the documentation that is suitable for your version of laravel.

Splitting laravel query returns massive odd result

I have this query in Laravel (5.3):
$menu_categories = DB::table('categories')->get();
It returns the categories from the database as expected. However, if I split it (so that I can add other parameters):
$menu_categories = DB::table('categories');
$menu_categories->get();
It returns a massive collection that starts like this:
Illuminate\Database\Query\Builder Object
(
[connection:protected] => Illuminate\Database\MySqlConnection Object
(
[pdo:protected] => PDO Object
(
)
[readPdo:protected] =>
[reconnector:protected] => Closure Object
(
[this] => Illuminate\Database\DatabaseManager Object...
What is happening here? I've searched for a while and can't find anything on this - although I admit I'm not sure what to search for. Note that I am NOT adding any additional parameters when it has the weird return collection - I saw the weird return initially with extra params (orderBy and whereIn) but even after removing those it still returns this weirdness.
The "non split" line is creating a query object for the categories table, calling get() on the query object and assigning the results of get() to the $menu_categories variable.
In the split version, the first line creates a query object for the categories table and sets it in the $menu_categories variable.
In the second line, calling get() on $menu_categories doesn't actually mutate the variable. Instead it returns the collection that you're looking for but it's not being assigned to anything. This is why you just see this massive object when you dump it.
The correct 2 line version would look something like this:
$query_object = DB:table('categories');
$menu_categories = $query_object->get();

How to update item conditionally with branch in RethinkDB

I am trying to do simple upsert to the array field based on branch condition. However branch does not accept a reql expression as argument and I get error Expected type SELECTION but found DATUM.
This is probably some obvious thing I've missed, however I can't find any working example anywhere.
Sample source:
var userId = 'userId';
var itemId = 'itemId';
r.db('db').table('items').get(itemId).do(function(item) {
return item('elements').default([]).contains(function (element) {
return element('userId').eq(userId);
}).branch(
r.expr("Element already exist"),
//Error: Expected type SELECTION but found DATUM
item.update({
elements: item('elements').default([]).append({
userId: 'userId'
})
})
)
})
The problem here is that item is a datum, not a selection. This happens because you used r.do. The variable doesn't retain information about where the object originally came from.
A solution that might seem to work would be to write a new r.db('db').table('items').get(itemId) expression. The problem with that option is the behavior isn't atomic -- two different queries might append the same element to the 'elements' array. Instead you should write your query in the form r.db('db').table('items').get(itemId).update(function(item) { return <something>;) so that the update gets applied atomically.

Duplicate parameter in web api

I have an issue regarding duplicate parameter in WebApi.
http://localhost:xxxxx/api/getbook?UserId=7 in this API Controller I have one parameter string UserId and works fine but if i do something like that
http://localhost:xxxxx/api/getbook?UserId=7?UserId=7 gets the result
So how to prevent duplicate parameter in API ?
A couple of things to note:
There would be an & character between the parameters i.e. http://localhost:xxxxx/api/getbook?UserId=7&UserId=7
The first parameter of the same name will be used by default if you are taking a model in to a controller method
If you are keen to throw some error when you discover duplicate parameter names then you could do the following:
var queryParameters = Request.GetQueryNameValuePairs()
.GroupBy(k => k.Key)
.Where(g => g.Count() > 1)
.Select(q => q.Key)
.ToList();
This will give you a List<string> of parameter names that appear more than once.
If you are interested in whether the parameter name and value combination is repeated (as in your example), then just GroupBy(k => k) instead and you will get a list of KeyValuePair to work with.

Grouping by anonymous v.s. grouping by non-anonymous

I need to group some data after it has arrived from server.
var result = context.GetData();
Assert.Equal(54, result.Count); // OK
var transactions = result.GroupBy(t => new //BeneficiaryGroup
{
BankAcronym = t.BankAcronym.Substring(4, 4),
DebitDate = t.DebitDate,
Key = t.Key
})
.OrderBy(g => g.Key.BankAcronym)
.ThenBy(g => g.Key.DebitDate)
.ThenBy(g => g.Key.Key)
.ToList();
Assert.Equal( 14, transactions.Count ); // OK
When I'm grouping by an anonymous object, the grouping is correctly done.
When I'm grouping by a BeneficiaryGroup object, with the exact same properties
public class BeneficiaryGroup
{
BankAcronym,
DebitDate,
Key
}
the grouping is not correctly done - the group has 54 records, as before grouping.
And I want to group the data by a class so I can return to the API consumer a collection already grouped.
Any ideas why this strange behaviour?
Anonymous types get 'sensible' equality behaviour 'for free', which is why the grouping works as you expect in that case. When you switch to using a named class, it then becomes your responsibility as the class definer to provide equality behaviour, to allow a BeneficiaryGroup to be used as a grouping key in the manner you expect.
As it says in the docs for GroupBy,
The default equality comparer Default is used to compare keys.
where Default is EqualityComparer<T>.Default, which explains:
The Default property checks whether type T implements the
System.IEquatable<T> interface and, if so, returns an
EqualityComparer<T> that uses that implementation. Otherwise, it
returns an EqualityComparer<T> that uses the overrides of
Object.Equals and Object.GetHashCode provided by T.
And for an anonymous type,
Because the Equals and GetHashCode methods on anonymous types are
defined in terms of the Equals and GetHashCode methods of the
properties, two instances of the same anonymous type are equal only if
all their properties are equal.
whereas for a named type that doesn't override Equals and GetHashCode, you will get the Object implementations, which generally aren't useful.

Resources