Rxjs distinct and arrays of numbers - rxjs

I can't explain to myself this
const something = new Rx.BehaviorSubject([1,2,4,4])
.distinct()
.do((s) => console.log(s))
.map(list => list.length)
.filter(length => length >=2)
.subscribe(total => console.log('total:', total));
this is what I get as output
[1, 2, 4, 4]
"total:"
4
I get confused because reviewing the docs on distinct I thought it would work for numbers. My use case is a data table widget sends me events and this array tracks which row they clicked and I want to detect once a double click occurred.
updated code
const something = new Rx.BehaviorSubject([]);
something.next([1]);
console.log(something.getValue());
something.next(something.getValue().concat(2));
something.next(something.getValue().concat(3));
something.next(something.getValue().concat(4));
something.next(something.getValue().concat(4));
something
.distinct()
.subscribe(val => console.log('value:', val));
output
"value:"
[1, 2, 3, 4, 4]

You're sending a value that happens to be an array. You would see the operation of distinct if you did
const something = new Rx.BehaviorSubject([]);
something .distinct() .subscribe(val => console.log('value:', val));
something.next(1); // --> value: 1
something.next(2); // --> value: 2
something.next(1); // no output (because of distinct)
something.next(3); // --> value: 3

Related

Cypress how to assert that console was calledWith array of x length?

I'm trying to find how to assert that the console log was called with an array of a defined fixed length and I'm not sure how to do that. I'm not very familiar with Mocha/Sinon, which Cypress seems to be using.
To give a bit more of a background of my App does, it's a Queued Editing, I change a few cell and they change color, once I'm ready to click save then it shows an array of all the cells that got changed (in my particular test, 11 got changed). I know how to test the entire calledWith comparison but I just want to test the array length and nothing more (since I have random data, I can't test an equal match of data but the array length will be sufficient).
So far I have this code that works for the most part except for the array length
describe('My Sample', () => {
beforeEach(() => {
// create a console.log spy for later use
cy.window().then((win) => {
cy.spy(win.console, 'log');
});
});
it('should click on the "Save" button and expect 2 console log calls with the queue items', () => {
cy.get('[data-test=save-all-btn]').click();
cy.get('.unsaved-editable-field')
.should('have.length', 0);
cy.window().then((win) => {
expect(win.console.log).to.have.callCount(2);
expect(win.console.log).to.be.calledWith(Array[11]); // how to test array length of 11?
});
});
Also, maybe as a second question, how could we assert that this array contains a certain object property with x value? In Jest we can do it this way. I just haven't seen anything similar in Cypress, is that kind of test possible in Cypress?
// for an array
const oddArray = [1, 3, 5, 7, 9, 11, 13];
test('should start correctly', () => {
expect(oddArray).toEqual(expect.arrayContaining([1, 3, 5, 7, 9]));
});
// for an array of objects
const users = [{id: 1, name: 'Hugo'}, {id: 2, name: 'Francesco'}];
test('we should have ids 1 and 2', () => {
expect(users).toEqual(
expect.arrayContaining([
expect.objectContaining({id: 1}),
expect.objectContaining({id: 2})
])
);
});

JSON is object instead of array, if array_diff returns assoc array on Collection->toArray()

My issue is in my json I am expecting an array, but am getting an object.
Details:
I have an array of numbers:
$numbers = [1];
I select from relationship, the "drawn numbers":
$drawnNumbers = Ball::whereIn('number', $numbers)->where('game_id', $card->game->id)->get()->map(function($ball) {
return $ball->number;
})->toArray();
I do a ->toArray() here. I want to find the numbers in $numbers that do not occur in $drawnNumbers. I do so like this:
$numbersNotYetDrawn = array_diff($numbers, $drawnNumbers);
My method then return $numbersNotYetDrawn (my headers accept is application/json).
So now the issue. When $drawnNumbers is an empty array, then the printed json is a regular array like this:
[
1
]
However if the relationship returns $drawnNumbers to be an array with numbers, then json is printed as an object:
{
"0" => 1
}
Does anyone know why this is? Anyway to ensure that json is array?
Edit:
Here is my actual data:
$drawnNumbers = Ball::whereIn('number', $numbers)->where('game_id', $card->game->id)->get()->map(function($ball) {
return $ball->number;
})->toArray();
$undrawnNumbers = array_diff($numbers, $drawnNumbers);
// $undrawnNumbers = array_values(array_diff($numbers, $drawnNumbers)); // temp fix
Replace
$numbersNotYetDrawn = array_diff($numbers, $drawnNumbers);
with
$numbersNotYetDrawn = array_values(array_diff($numbers, $drawnNumbers));
to make sure element keys are reset and array is treated as a simple list and serialized to a JSON list - instead of being treated as an associative array and serialized to a JSON object.
I recently had this same problem and wondered the same thing.
I solved it by adding "array_values", but I was wondering how to reproduce it.
I found it that it is reproduced when array_diff removes an element from the array that isn't the last element. So:
>>> $x
=> [
1,
2,
3,
4,
5,
]
>>> array_diff($x, [5]);
=> [
1,
2,
3,
4,
]
>>> array_diff($x, [1]);
=> [
1 => 2,
2 => 3,
3 => 4,
4 => 5,
]

RxJs. Combining latest and once

I have a UI like this:
Where I can
Enter something in search box
Drag users from table to chart
The logic required is:
Initially chart shows some subset of all users (e.g., first 10)
When users are dragged on chart they are added to the users that already there
When filter is applied all users are removed from chart and then it is repopulated with some subset of matching users
I am trying to implement such logic with RxJs.
I have filteredUsers$ and addedUsers$ stream that produce users matching filter and dragged users correspondingly.
I need to combine them in such way:
Observable
.<OPERATOR>(filteredUsers$, addedUsers$)
.subscribe(([filteredUsers, addedUsers]) => {
// When filteredUsers$ fires:
// filteredUsers is value from stream
// addedUsers == null
// When addedUsers$ fires:
// filteredUsers is latest available value
// addedUsers is value from stream
redrawChart(/* combining users */)
});
Any ideas how I can achieve this?
Time sequence:
Filtered: - a - - - - a - ->
Added : - - b - b - - - ->
Result : - a ab - ab - a - ->
If you want the final stream to be populated only when addUsers$ fires with latest from could be a solution:
So, in your case addUsers$ could be the first stream.
You can try out the following code:
let firstObservable$ = Observable.from([1, 2, 3, 4])
.zip(Observable.interval(50), (a, b) => {
return a;
});
let secondObservable$ = Observable.from([5, 6])
.zip(
Observable.interval(70), (a, b) => {
return a;
});
firstObservable$
.withLatestFrom(secondObservable$, (f, s) => ({ a: f, b: s }))
.subscribe(x => {
console.log('result: ', x);
});
The first observable emits every 50 ms a value from the array.
The second observable every 75 ms.
The values printed are {a: 2, b: 5} {a: 3, b: 6} {a: 4, b: 6}
Because 1 was emitted before 5 we lose the pair (1,5)!
I am not clear but missing a pair from addUsers$ if the other stream has not emitted may be non-desired behavior for you.
You could overcome that if you start the second stream with an initial value and then filter out any results you don't want.
You have the combineLatest operator which basically does what you are describing. It combines two observables and gives you the latest value of both streams.
So:
--a--b-----c---
-x-----d-----p-
-combineLatest-
--a--b-b---c-c
x x d d p
This should allow you to do what you want if I understand correctly.
Here's the official doc link:
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/combinelatest.md
Eventually I have done it by adding additional subject:
var filteredUsers$ = ... // users from filter
var addedUsers$ = ... // users dragged on chart
var usersToDraw$ = new Subject();
subscriptions:
usersToDraw$
.subscribe(usersToDraw => {
redrawChart(usersToDraw);
});
filteredUsers$
.subscribe(filteredUsers => {
usersToDraw$.next(filteredUsers);
});
Observable
.combineLatest(filteredUsers$, addedUsers$)
.filter(([filteredUsers, addedUsers]) => addedUsers != null)
.subscribe(([filteredUsers, addedUsers]) => {
// we 'clear' stream so the same users won't be added twice
addedUsers$.next(null);
usersToDraw$.next(/* combining users */);
});
UPDATE
The solution can be improved with withLatestFrom (thanks #nova)
usersToDraw$
.subscribe(usersToDraw => {
redrawChart(usersToDraw);
});
filteredUsers$
.subscribe(filteredUsers => {
usersToDraw$.next(filteredUsers);
});
addedUsers$
.withLatestFrom(filteredUsers$)
.subscribe(([addedUsers, filteredUsers]) => {
usersToDraw$.next(/* combining users */);
});

LINQ Get Grouped ID by condition

Hi I have a List so:
A 1
A 2
A 3
A 4
B 1
B 2
C 1
I want to select the letter that contains AT LEAST these 3 numbers: 1,2,3
So in this case would be selected the letter A.
Can you help me to write this as LINQ expression?
Thanks a lot!
First, make a collection of the numbers you require.
var required = new[] { 1, 2, 3 };
Then, group your pairings by letter.
var groupedPairings = pairings.GroupBy(p => Letter, p => Number);
Then, discard those pairings that don't have your three required items. (The logic here is "take the collection of required items, remove anything in the group, and make sure there is nothing left".)
var groupsWithRequired = groupedPairings
.Where(g => !required.Except(g).Any());
Now, if you just want the letters, you can simply do
var lettersWithRequired = groupsWithRequired.Select(g => g.Key);
or if you want a dictionary mapping from the letter to its collection of numbers, you can do
var dictionary = groupsWithRequired.ToDictionary(g => g.Key, g => g.ToArray());
var numbersForA = dictionary["A"]; // = {1, 2, 3, 4}
You could try this, although I don't feel it's the best answer:
var items = new List<Item>{
new Item{Name="A", Value=1},
new Item{Name="A", Value=2},
new Item{Name="A", Value=3},
new Item{Name="A", Value=3},
new Item{Name="A", Value=4},
new Item{Name="B", Value=1},
new Item{Name="B", Value=2},
new Item{Name="C", Value=1},
};
var values = new List<int>{1,2,3};
var query = items.GroupBy (i => i.Name)
.Where (i => i.Select (x => x.Value)
.Intersect(values).Count() == values.Count)
.Select (i => i.Key);
Where
class Item{
public string Name{get;set;}
public int Value{get;set;}
}

Filter Enums based on List in LINQ

I Have a Enum
public enum ProcessStatus: byte
{
NotStarted = 0,
PreCheckStarted= 1,
PreCheckCompleted= 2,
Processing= 3,
Failed= 4,
Completed= 5,
Closed= 6
}
in Table we have entries like 0,3,5,6
we need list of Enums based on some criteria and criteria is List which contains 0,1,2
i am able to get all Enums as List Like
Enum.GetValues(typeof(ProcessStatus)).OfType<ProcessStatus>()
and have
List<byte> processListIDs
which contains IDs
i want
IEnumerable<ProcessStatus> filtered based on ids in processListIDs using LINQ.
Thanks in Advance
You can use Intersect with better performance:
var enumList = Enum.GetValues(typeof (ProcessStatus))
.OfType<ProcessStatus>().Cast<byte>();
var result = enumList.Intersect(processListIDs)
.Cast<ProcessStatus>();
var res =
processStatusCollection.Where(item => processListIDs.Contains((int)item));
You could use Enum.TryParse<TEnum>:
List<byte> processListIDs = new List<byte>() { 0, 3, 5, 6 };
ProcessStatus ps = ProcessStatus.NotStarted;
IEnumerable<ProcessStatus> status = processListIDs
.Where(p => Enum.TryParse<ProcessStatus>(p.ToString(), out ps))
.Select(p => ps);
Try this,
var p = new List<byte>() { 1, 2, 3, 4, 6 };
IEnumerable<ProcessStatus> result = p.Select(o => (ProcessStatus)Enum.Parse(typeof(ProcessStatus), o.ToString()));
/// do something with result

Resources