Last active
December 28, 2015 15:49
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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