Skip to content

Instantly share code, notes, and snippets.

@vy-let
Last active August 29, 2015 14:16
Show Gist options
  • Save vy-let/224301c5ac64fd1aee3e to your computer and use it in GitHub Desktop.
Save vy-let/224301c5ac64fd1aee3e to your computer and use it in GitHub Desktop.
Bringing the sliding window metaphor to RACStreams (RACSequences, RACSignals, etc).
//
// RACStream+SlidingWindow.m
// Bringing the sliding window stream metaphor to RACStreams (RACSequences, RACSignals, etc).
// Copyright © 2015 Talus Baddley. MIT License.
//
// This is heavily UNTESTED. Please contact me if you find bugs.
//
#import <ReactiveCocoa/ReactiveCocoa.h>
@interface RACStream (SlidingWindow)
//
// Takes a sliding window of the receiver, with as many as <size> elements,
// but no fewer than <minCount> elements. Each element in the stream is an NSArray.
//
// For instance, the sequence
// --(1)--(2)----(3)------(4)------(5)------
// with size 3 and minimum 2, produces arrays
// -------[1,2]--[1,2,3]--[2,3,4]--[3,4,5]--
//
- (instancetype)slidingWindowSized:(NSUInteger)size minimumCount:(NSUInteger)minCount;
//
// Takes a sliding window as above, with no minimum.
- (instancetype)slidingWindowSized:(NSUInteger)size;
//
// Takes a sliding window of the receiver at exactly <size> elements wide. Each element in
// the stream is a RACTuple. If there are fewer than <size> elements in the window, nils pad
// the head of the tuple. (If there are fewer than <minCount> elements, no tuple is produced.)
//
// For instance, the sequence
// --(1)----------(2)--------(3)------(4)------(5)------
// with size 3 and minimum 1, produces tuples
// --[nil,nil,1]--[nil,1,2]--[1,2,3]--[2,3,4]--[3,4,5]--
//
- (instancetype)nilPaddedSlidingWindowSized:(NSUInteger)size minimumCount:(NSUInteger)minCount;
@end
@implementation RACStream (SlidingWindow)
- (instancetype)slidingWindowSized:(NSUInteger)size {
return [self slidingWindowSized:size minimumCount:0];
}
- (instancetype)slidingWindowSized:(NSUInteger)size minimumCount:(NSUInteger)minCount {
// Based on https://github.com/baconjs/bacon.js/blob/master/src/slidingwindow.coffee
return [[self
scanWithStart:@[] reduce:^id(NSArray *running, id next) {
NSRange sliceRange = ([running count] >= size ?
NSMakeRange(1, size) :
NSMakeRange(0, [running count] + 1)
);
return [[running arrayByAddingObject:next] subarrayWithRange:sliceRange];
}]
filter:^BOOL(NSArray *window) {
return [window count] >= minCount;
}];
}
- (instancetype)nilPaddedSlidingWindowSized:(NSUInteger)size minimumCount:(NSUInteger)minCount {
return [[self slidingWindowSized:size minimumCount:minCount]
map:^id(NSArray *flexWindow) {
NSInteger sizeDiff = size - [flexWindow count];
NSMutableArray *buf = [NSMutableArray arrayWithCapacity:size];
for (NSUInteger fuckingManualIteration = 0; fuckingManualIteration < sizeDiff; fuckingManualIteration++) {
[buf addObject:[RACTupleNil tupleNil]];
}
// Or, as you'd say in Ruby, Array.new(sizeDiff, nil)
[buf addObjectsFromArray:flexWindow];
return [RACTuple tupleWithObjectsFromArray:buf];
}];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment