Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(skipLast): add skipLast operator #2316

Merged
merged 4 commits into from
May 9, 2017

Conversation

martinsik
Copy link
Contributor

@martinsik martinsik commented Jan 30, 2017

  • Add the operator to Rx
  • It must have a -spec.ts tests file covering the canonical corner cases, with marble diagram tests
  • If possible, write a asDiagram test case too, for PNG diagram generation purposes
  • The operator must be documented in JSDoc style in the implementation file, including also the PNG marble diagram image
  • The operator should be listed in doc/operators.md in a category of operators
  • It should also be inserted in the operator decision tree file doc/decision-tree-widget/tree.yml
  • You may need to update MIGRATION.md if the operator differs from the corresponding one in RxJS v4

Description:

Adds skipLast operator from RxJS 4. Its internals and tests are based on takeLast for better performance.

Related issue (if exists):

Closes #1404

Adds skipLast operator from RxJS 4. Its internals and tests are based on takeLast for better

performance.

Closes ReactiveX#1404
@martinsik
Copy link
Contributor Author

I'm actually not sure what the last unchecked bullet point means:

The spec file should have a type definition test at the end of the spec to verify type definition for various use cases

Is there any other operator that already tests this?

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 97.713% when pulling 0940b14 on martinsik:skiplast-operator into d4533c4 on ReactiveX:master.

@staltz
Copy link
Member

staltz commented Jan 30, 2017

You can ignore that task, it used to be needed.

children:
- label: based on a given amount
children:
- label: skipLast
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because there is only one child under "from the end of the Observable", I suggest you combine them into one. So:

- label: from the end of the Observable
  children:
  - label: skipLast

asDiagram('skipLast(2)')('should skip two values of an observable with many values', () => {
const e1 = cold('--a-----b----c---d--|');
const e1subs = '^ !';
const expected = '-------------a---b--|';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is something really wrong about this marble diagram. The bottom a should not be emitted at the same time as the top c, because when c occurs, we don't actually yet know that it's the second-last emission. We can only know that when the e1 completes. So the expected should be (ab|) when the source completes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As soon as c is emitted, it is known that a should not be skipped because it is not one of the last 2 elements and can be emitted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, funny. I may have misunderstood skipLast. Is this how RxJS 4 works?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this looks correct. if the skipLast(n) buffer grows to n + 1, we know we can drain to n and emit.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.003%) to 97.699% when pulling 34eab36 on martinsik:skiplast-operator into d4533c4 on ReactiveX:master.

*/
class SkipLastSubscriber<T> extends Subscriber<T> {
private ring: T[] = [];
private count: number = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things:

  1. rename these to start with underscores. I know we're not doing that everywhere, but we should be with private and protected properties as a signal to non-TS users.
  2. Rename total to _skipCount or _numberToSkip, something more descriptive.
  3. We should be initializing the ring buffer in the ctor to be the same size as the _skipCount, it should perform better than constantly using push.
  4. Instead of keeping a _count, we can keep a _currentIndex or _index, such that when we increment it we do: this._currentIndex = (_currentIndex + 1) % _skipCount; That way it doesn't inflate past where it needs to be.

Copy link
Contributor Author

@martinsik martinsik Feb 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Blesh I think we have to keep _count instead of just _currentIndex because we need to know when to start remitting buffered items since the buffer is now fixed size.

const idx = this.count++ % this.total;
const oldValue = this.ring[idx];

this.ring[idx] = value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be sure if your accessing the same property more than once to pull it out into a const. That way we don't have the cost of looking it up over and over.

@benlesh
Copy link
Member

benlesh commented Feb 2, 2017

Overall this looks good. I don't think skipLast is trivial to implement using existing operators, so this is a solid candidate to re-add to the library. I've just requested a few changes, WRT to performance and naming conventions. (Please do check the perf before and after the changes).

@benlesh
Copy link
Member

benlesh commented Feb 2, 2017

Also, it seems the files added have 100% coverage, so I'm not worried about coveralls complaining.

@martinsik
Copy link
Contributor Author

martinsik commented Feb 4, 2017

After the changes performance is roughly the same:

Before:

                                         |                     RxJS 4.1.0 |                     RxJS 5.0.3 |          factor |      % improved
---------------------------------------------------------------------------------------------------------------------------------------------------
                    skiplast - immediate |                 3,154 (±1.23%) |                40,651 (±1.04%) |          12.89x |        1,189.0%
                                skiplast |                 1,442 (±2.35%) |                18,099 (±1.55%) |          12.55x |        1,155.2%

After:

                                         |                     RxJS 4.1.0 |                     RxJS 5.0.3 |          factor |      % improved
---------------------------------------------------------------------------------------------------------------------------------------------------
                    skiplast - immediate |                 3,218 (±2.27%) |                42,526 (±0.80%) |          13.21x |        1,221.4%
                                skiplast |                 1,497 (±2.31%) |                19,353 (±1.75%) |          12.92x |        1,192.5%

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.002%) to 97.7% when pulling 83ed187 on martinsik:skiplast-operator into d4533c4 on ReactiveX:master.

@benlesh
Copy link
Member

benlesh commented Feb 16, 2017

@martinsik ... this is great! I'll merge this, but it'll have to wait for the next minor. I think I'd like to get the bug fixes out first in a patch. So it might be a few weeks.

@benlesh benlesh merged commit 4ffbbe5 into ReactiveX:master May 9, 2017
@benlesh
Copy link
Member

benlesh commented May 9, 2017

Thank you @martinsik!

@lock
Copy link

lock bot commented Jun 6, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants