How can I simplify my nested for-loops with ReactiveCocoa?

for-loop, mapreduce, objective-c, reactive-cocoa, reactive-programming

Solution

You basically would like to create the cartesian product of the dictionaries and make a selection on it. There is no default operator in ReactiveCocoa that I know of that would do this for you. (In LINQ there are operators for this.) In RAC the simplest solution is to use the `scanWithStart:combine:` method to implement this operation. Once the cartesian is ready, the `filter:` and `take:1` operations will produce the sequence of your choice.

NSDictionary *adic = @{@"aa":@"vb", @"ab": @"va"};
NSDictionary *bdic = @{@"ba": @"va", @"bb":@"vb"};;

RACSequence *aseq = adic.rac_keySequence;
RACSequence *bseq = bdic.rac_keySequence;

RACSequence *cartesian = [[aseq scanWithStart:nil combine:^id(id running, id next_a) {
    return [bseq scanWithStart:nil combine:^id(id running, id next_b) {
        return RACTuplePack(next_a, next_b);
    }];
}] flatten];

RACSequence *filteredCartesian = [cartesian filter:^BOOL(RACTuple *value) {
    RACTupleUnpack(NSString *key_a, NSString *key_b) = value;
    // business logic with keys
    return false;
}];

RACSequence *firstMatch = [filteredCartesian take:1];

Problem

Say I have 2 `NSDictionaries` that I don't know beforehand like: ``` NSDictionary *dictA = @{ @"key1" : @1, @"key2" : @2 }; NSDictionary *dictB = @{ @"key1" : @"a string" }; ``` I want to find the first match between the keys of `dictB` and the keys or values of `dictA`. Each key of `dictB` will either be a NSNumber or a string. If it's a number, try to find a match from the values of `dictA`. If it's a string, try to find a match from the keys of `dictA`. Using for loops, it would looks something like this: ``` id match; for (id key in dictA ) { for (id _key in dictB { if ( [_key is kindOfClass:NSNumber.class] && _key == dictA[key] ) { match = _key goto outer; } else if ( [_key is kindOfClass:NSString.class] && [_key isEqualToString:key] ) { match = _key goto outer; } } }; outer:; NSString *message = match ? @"A match was found" : @"No match was found"; NSLog(message); ``` How could I rewrite this with ReactiveCocoa using `RACSequence` and `RACStream` methods so it looks something like: ``` // shortened pseudo code: // id match = [dictA.rac_sequence compare with dictB.rac_sequence using block and return first match]; ```

Original source