-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Conversation
Adds skipLast operator from RxJS 4. Its internals and tests are based on takeLast for better performance. Closes ReactiveX#1404
I'm actually not sure what the last unchecked bullet point means:
Is there any other operator that already tests this? |
You can ignore that task, it used to be needed. |
doc/decision-tree-widget/tree.yml
Outdated
children: | ||
- label: based on a given amount | ||
children: | ||
- label: skipLast |
There was a problem hiding this comment.
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--|'; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
src/operator/skipLast.ts
Outdated
*/ | ||
class SkipLastSubscriber<T> extends Subscriber<T> { | ||
private ring: T[] = []; | ||
private count: number = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few things:
- 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.
- Rename
total
to_skipCount
or_numberToSkip
, something more descriptive. - We should be initializing the ring buffer in the ctor to be the same size as the
_skipCount
, it should perform better than constantly usingpush
. - 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.
There was a problem hiding this comment.
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.
src/operator/skipLast.ts
Outdated
const idx = this.count++ % this.total; | ||
const oldValue = this.ring[idx]; | ||
|
||
this.ring[idx] = value; |
There was a problem hiding this comment.
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.
Overall this looks good. I don't think |
Also, it seems the files added have 100% coverage, so I'm not worried about coveralls complaining. |
After the changes performance is roughly the same: Before:
After:
|
@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. |
Thank you @martinsik! |
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. |
-spec.ts
tests file covering the canonical corner cases, with marble diagram testsasDiagram
test case too, for PNG diagram generation purposesdoc/operators.md
in a category of operatorsdoc/decision-tree-widget/tree.yml
MIGRATION.md
if the operator differs from the corresponding one in RxJS v4Description:
Adds skipLast operator from RxJS 4. Its internals and tests are based on takeLast for better performance.
Related issue (if exists):
Closes #1404