Skip to content

Instantly share code, notes, and snippets.

@dismory
Last active December 28, 2015 15:49
Show Gist options
  • Save dismory/7524920 to your computer and use it in GitHub Desktop.
Save dismory/7524920 to your computer and use it in GitHub Desktop.
A full and runnable code example for the question on Stack Overflow: http://stackoverflow.com/q/19862609/492790
#import <Foundation/Foundation.h>
#import <ReactiveCocoa/ReactiveCocoa.h>
int main(int argc, const char * argv[])
{
@autoreleasepool {
RACSubject *input = [RACSubject subject];
RACSignal *output = [[[[input
map:^(NSString *combo) {
NSArray *components = [combo componentsSeparatedByString:@","];
NSInteger number = [components[1] integerValue];
return RACTuplePack(components[0], @(number));
}]
// !!!: DIFF
// We need there state parameters:
// 1. The letters and numbers we're waiting for.
// 2. Values received that cannot be forwarded until a certain
// letter/number.
// 3. The values to forward at each step.
scanWithStart:RACTuplePack(@{ @"A": @1 }, @[], @[]) reduce:^id(RACTuple *state, RACTuple *letterAndNumber) {
__block NSDictionary *waitingLettersAndNumbers = state[0];
__block NSArray *queuedValues = state[1];
// Enqueue this value until we're ready to send it (which may or may not
// occur on this step of the scan).
queuedValues = [queuedValues arrayByAddingObject:letterAndNumber];
char letterChar = [letterAndNumber.first characterAtIndex:0];
if (
[[waitingLettersAndNumbers objectForKey:letterAndNumber.first] isEqualToNumber:letterAndNumber.second]
&&
(letterChar == 'A' || [[waitingLettersAndNumbers objectForKey:[NSString stringWithFormat:@"%c", letterChar - 1]] integerValue] > [letterAndNumber.second integerValue])
) {
NSMutableDictionary *mutableWaitingLettersAndNumbers = [NSMutableDictionary dictionaryWithDictionary:waitingLettersAndNumbers];
// Sort queuedValues lexically and numerically.
__block NSUInteger cursor = 0;
NSUInteger length = [queuedValues count];
while (cursor < length) {
NSMutableArray *array = [NSMutableArray arrayWithArray:queuedValues];
RACTuple *tuple = [queuedValues objectAtIndex:cursor];
NSString *letter = tuple.first;
NSString *number = tuple.second;
[queuedValues enumerateObjectsUsingBlock:^(RACTuple *tuple, NSUInteger idx, BOOL *stop) {
if ([letter isEqualToString:tuple.first] && number.integerValue > [tuple.second integerValue] && idx > cursor) {
[array removeObjectAtIndex:idx];
[array insertObject:tuple atIndex:cursor];
*stop = YES;
}
if (idx == length - 1) {
cursor = cursor + 1;
}
}];
queuedValues = [array copy];
}
cursor = 0;
while (cursor < length) {
NSMutableArray *array = [NSMutableArray arrayWithArray:queuedValues];
RACTuple *tuple = [queuedValues objectAtIndex:cursor];
NSString *letter = tuple.first;
NSNumber *number = tuple.second;
[queuedValues enumerateObjectsUsingBlock:^(RACTuple *tuple, NSUInteger idx, BOOL *stop) {
if ([number isEqualToNumber:tuple.second] && [letter compare:tuple.first] == NSOrderedDescending && idx > cursor) {
[array removeObjectAtIndex:idx];
[array insertObject:tuple atIndex:cursor];
*stop = YES;
}
if (idx == length - 1) {
cursor = cursor + 1;
}
}];
queuedValues = [array copy];
}
// Determine the next letter and number.
NSMutableArray *forwardValues = [NSMutableArray array];
NSMutableArray *remindValues = [NSMutableArray array];
[queuedValues enumerateObjectsUsingBlock:^(RACTuple *tuple, NSUInteger idx, BOOL *stop) {
NSString *letter = tuple.first;
NSNumber *number = tuple.second;
char letterChar = [letter characterAtIndex:0];
if ([[mutableWaitingLettersAndNumbers objectForKey:letter] isEqualToNumber:number]) {
[forwardValues addObject:tuple];
// Current matching letter's number + 1
[mutableWaitingLettersAndNumbers setObject:@(number.integerValue + 1) forKey:letter];
// Update next letter's number when it's `nil`.
// After that the next letter's number can only be updated
// by the same letter.
if (letterChar < 'Z') {
NSString *nextLetter = [NSString stringWithFormat:@"%c", letterChar + 1];
if ([mutableWaitingLettersAndNumbers objectForKey:nextLetter] == nil) {
[mutableWaitingLettersAndNumbers setObject:@1 forKey:nextLetter];
}
}
}
else {
[remindValues addObject:tuple];
}
}];
queuedValues = [remindValues copy];
waitingLettersAndNumbers = [mutableWaitingLettersAndNumbers copy];
// !!!: DIFF
// After forwarding some values, we may still have some queued values.
return RACTuplePack(waitingLettersAndNumbers, queuedValues, forwardValues);
} else {
// No values should escape the scan yet. Just pass on our queued
// values.
return RACTuplePack(waitingLettersAndNumbers, queuedValues, @[]);
}
}]
map:^(RACTuple *state) {
// Convert the array of values into a signal.
NSArray *forwardValues = state.last;
return [forwardValues.rac_sequence signalWithScheduler:[RACScheduler immediateScheduler]];
}]
// Forward values from each inner signal in the correct, sorted order.
concat];
[output subscribeNext:^(RACTuple *tuple) {
NSLog(@"(%@,%@)", tuple.first, tuple.second);
}];
[input sendNext: @"A,2"]; // Expect no output
[input sendNext: @"B,4"]; // Expect no output
[input sendNext: @"B,2"]; // Expect no output
[input sendNext: @"B,1"]; // Expect no output
[input sendNext: @"A,1"]; // Expect output: (A,1) (A,2) (B,1) (B,2)
// Note: (A,1) (B,1) (B,2) (A,2) would *not* be right because A,2 appeared on the input before B,1
[input sendNext: @"C,1"]; // Expect output: (C,1)
[input sendNext: @"B,3"]; // Expect output: (B,3) (B,4)
[input sendNext: @"C,3"]; // Expect no output
[input sendNext: @"C,2"]; // Expect output: (C,2) (C,3)
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment