RACSignal: how to reduce on arbitrarily large combine - objective-c

Consider an example (paraphrased) from the ReactiveCocoa Introduction, which
enables based on whether the .password and .passwordConfirm text fields match:
RAC(self.enabled) = [RACSignal
combineLatest:#[ RACAble(self.password), RACAble(self.passwordConfirm) ]
reduce:^(NSString *password, NSString *passwordConfirm) {
return #([passwordConfirm isEqualToString:password]);
}];
Here we know at compile time how many and what things we are combining, and it is useful to destructure/map the "combine" array into multiple arguments to the reduce block. What about when that won't work. For instance, if you want:
RAC(self.enabled) = [RACSignal
combineLatest:arrayOfSignals
reduceAll:^(NSArray *signalValues) { // made this up! don't try at home.
// something ...
}];
How do you do this with ReactiveCocoa?
UPDATE: the accepted answer's comments help explain what I was missing.

You can use map:
RAC(self.enabled) = [[RACSignal combineLatest:arrayOfSignals]
map:^(RACTuple *signalValues) {
// something
}
];
A RACTuple can be manipulated in many ways, it conforms NSFastEnumeration, it has the allObjects method and also the rac_sequence method. You can for example combine all boolean values this way:
RAC(self.enabled) = [[RACSignal combineLatest:arrayOfSignals]
map:^(RACTuple *signalValues) {
return #([signalValues.rac_sequence all:^BOOL(NSNumber *value) {
return [value boolValue];
}]);
}
];
Hope it helps.

Related

Is there a way to set restrictions on arc4random()'s results?

I'm making three random choices between two classes, A and B. I need to avoid getting B all three times.
Is there a way to stop arc4random() from giving me that result?
One approach is: If your random routine gives you an unacceptable answer, run it again until it gives you an acceptable answer.
For example, in a solitaire game app of mine, I shuffle a deck and deal some of it into a layout which must be solvable. But what if it isn't solvable? I repeat that: I shuffle the deck again and deal it again. I keep doing that until the layout I've dealt is solvable. All of that happens before the user sees anything, so the user doesn't know what I've been up to behind the scenes to guarantee that the layout makes sense.
In your case, where the possibilities are so limited, another obvious alternative would be this: use a simple combinatorial algorithm to generate beforehand all acceptable combinations of three nodes. Now use arc4random to pick one of those combinations. So, for example:
var list = [[String]]()
let possibilities = ["A","B"]
for x in possibilities {
for y in possibilities {
for z in possibilities {
list.append([x,y,z])
}
}
}
list.removeLast()
Now list is an array of all possible triples of "A" or "B", but without ["B","B","B"]. So now pick an element at random and for each of its letters, if it is "A", use class A, and if it is "B", use class B. (Of course I suppose we could have done this with actual classes or instances, but it seems simplest to encode it as letters.)
BOOLs and loops to the rescue...
BOOL classA = false;
BOOL classB = false;
for (int i=0; i<3; i++) {
int r = arc4random() % 2;
if(i < 2) {
if(r == 0) {
NSLog(#"Class A");
classA = true;
} else {
NSLog(#"Class B");
classB = true;
}
} else {
if(classA == false)
NSLog(#"Class A");
if(classB == false)
NSLog(#"Class B");
}
}
The 2 BOOLs guarantee that each class has at least one member for each 3 cycle run.

Objective C: Using a BOOL to return 'YES' if a condition can be applied

Hi there I have some code at the moment that gives me the error ("0") is not equal to ("50") - condition not applied correctly Basically I am currently using a traditional for loop within a BOOL which goes through the list of items and checks whether or not the condition can be applied, if it can be applied to an item then the BOOL will return YES. I cannot see where I am currently going wrong and need guidance. My code is shown below:
-(BOOL)conditionCanBeApplied:(NSArray *)items{
bool itemConditionCanBeApplied = NO;
for (int i = 0; i < items.count; i++)
{
id myItem = [[items valueForKeyPath:#"itemId"]objectAtIndex: i];
if ([self.applicableItems containsObject:myItem]) {
itemConditionCanBeApplied = YES;
}
}
return itemConditionCanBeApplied;
}
First, don't mix BOOL and bool, they might be very similar but they aren't the same data type. Second, always use fast enumeration if you have a choice. I am assuming in the code below that your items collection is something like an NSArray. Also, there is no reason to test with an if statement just to set a BOOL since the test is a boolean statement. (I am doing it in my example to allow for the break) Lastly, short-circuiting your logic with a break keeps the processor from doing unnecessary work once you have at least one match.
Do something like this:
- (BOOL)conditionTest
{
BOOL itemConditionCanBeApplied = NO;
for (id item in items) {
if ([self.applicableItems containsObject:item]) {
itemConditionCanBeApplied = YES;
break;
}
}
return itemConditionCanBeApplied;
}

For with multiple in

I want to use the basic Objective C stament for (id object in collection) with multiple objects and conditions like this:
for (Origin *origin in [self.fetchedOriginController fetchedObjects] AND Destiny *destiny in [self.fetchedDestinyController fetchedObjects]))
{
NSLog(#"This route starts from %# and ends in %#, origin.name, destiny.name);
}
So the log would be:
This route starts in London and ends in Sidney
This route starts in Madrid and ends in Barcelona
This route starts in Washington and ends in Vienna
(...)
How can this be done?
If you are looking for all combinations just nest:
for (object in collection)
{
for (object2 in collection2)
{
...
}
}
Or are you looking for pairs of objects from two same sized collections? If so create a loop which provides the index:
NSUInteger count = collection.count;
for (NSUInteger ix = 0; ix < count; ix++)
{
id object = collection[ix];
id object2 = collection2[ix];
...
}
If you want to loop over the common pairs of two different sized collections just change the first line to:
NSUInteger count = MIN(collection.count, collection2.count);
If you want something else edit your question to be more explicit.
Something like this would work for what you're trying I think:
for (NSUInteger i = 0; i < collection1.count; i++) {
id object1 = collection1[i];
id object2 = collection2[i];
// continue ...
}
As far as I know, there's no built in syntax to do what you're asking.
This solution makes the assumption that collection1 and collection2 have the same amount of objects, or at least collection2 doesn't exceed collection1 in count. Given your desired syntax, I think you have already planned for this, I just wanted to mention it in case someone else stumbles on this.
What order do you expect this enumeration to happen in? What kind of association between objects from each collection are you looking for?
If you want to take each object from collection and do something with both that object and each object2 in collection2, use nested loops:
for (object in collection) {
for (object2 in collection2) {
// ...
}
}
If you want to go through both collections in order, use two loops.
for (object in collection) { /* ... */ }
for (object2 in collection2) { /* ... */ }
If you expect some sort of mapping between the objects in collection and collection2 (which presumes they have the same count and at least one of them has unique entries), you might look into a data structure that captures that mapping. Then you can iterate across pairs.
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:collection2 forKeys:collection];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){
// ...
}];

NSSharingService: How to compare with the constants?

I want to do something when the user shared to
- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items
{
BOOL isSafariReadingList = [sharingService.description rangeOfString:NSSharingServiceNameAddToSafariReadingList].location != NSNotFound;
}
I can't find any property on NSSharingService that I can compare to the constants. Am I missing something?!
Since it seems there's no answer to that (filed http://openradar.appspot.com/16114564), I created a category
#implementation NSSharingService (ActivityType)
- (NSString*)activityType {
NSRange range = [self.description rangeOfString:#"\\[com.apple.share.*\\]" options:NSRegularExpressionSearch];
range.location++; // Start after [
range.length -= 2; // Remove both [ and ]
return [self.description substringWithRange:range];
}
#end
then you can use it these ways:
[[sharingService activityType] isEqualToString:NSSharingServiceNameAddToSafariReadingList];
[#[NSSharingServiceNameAddToSafariReadingList, NSSharingServiceNameAddToIPhoto]
containsObject:[sharingService activityType]];
I guess Apple just didn't think we'd want to know which service people picked from the Picker.
Obviously dangerous to parse that “com.apple.share.System” but it’s the only way I see to avoid a set of rangeOfString || rangeOfString || rangeOfString…..
For more information check out https://github.com/stuffmc/SharingPicker
Also, as a reference, here are some of the values of those NSSharingServiceName* constants, all starting with com.apple.share.
PostOnFacebook Facebook.post
PostOnTwitter Twitter.post
PostOnSinaWeibo SinaWeibo.post
PostOnTencentWeibo TencentWeibo.post
PostOnLinkedIn LinkedIn.post
ComposeEmail Mail.compose
ComposeMessage Messages.compose
SendViaAirDrop AirDrop.send
UseAsTwitterProfileImage Twitter.set-profile-image
UseAsFacebookProfileImage Facebook.set-profile-image
UseAsLinkedInProfileImage LinkedIn.set-profile-image
PostImageOnFlickr Video.upload-image-Flickr
AddToSafariReadingList System.add-to-safari-reading-list
AddToIPhoto System.add-to-iphoto
An equivalent of this check works in my Swift code:
sharingService == NSSharingService(named: NSSharingServiceNameAddToSafariReadingList)

Expected expression before 'unsigned' ->Objective C

m_cAppIdMap is an object of a dictionary.
I want to iterate through the dictionary and to ind and remove the value pEvent.wTimerId is an unsigned short integer that is stored as key in the dictionary.
if(unsigned short* key in m_cAppIdMap) //error:Expected expression before 'unsigned'
{
(void)[self findAndRemoveEvent:pEvent];
(void)CFDictionaryRemoveValue(m_cAppIdMap,&wTimerId);
free(pEvent);
bReturn = YES;
}
I am getting an error when i try to iterate through the loop.
EDITED
-(BOOL)KillTimer:(unsigned short)wTimerId
{
stRs232Timer* pEvent;
BOOL bReturn=FALSE;
theLock = [[NSLock alloc]init];
if ([theLock tryLock]) {
// if ( m_cAppIdMap.Lookup(wTimerId,pEvent) )
// {
// (void)findAndRemoveEvent(pEvent); // remove from event queue
// (void)m_cAppIdMap.RemoveKey(wTimerId); // remove from app map
for(wTimerId in m_cAppIdMap)
{
(void)[self findAndRemoveEvent:pEvent];
(void)CFDictionaryRemoveValue(m_cAppIdMap,&wTimerId);
free(pEvent);
bReturn = YES;
}
[theLock unlock];
}
return bReturn;
}
I am getting error in this code 'selector element does not have a valid object type' . I need to search for wTimerId(key) in the m_cAppIdMap. Is it what i'm doing is correct.The commented lines above the for loop is the implementation of the same code in cpp. I coud not make the same logic over here in Objective C.
I think you meant to use for rather than if. Additionally, the fast enumeration syntax
for (x in y) can only be used on objects that implement the NSFastEnumeration protocol—typically NSArray. It looks like you're using C arrays, so this syntax won't work anyway.
you meant to write for (VARIABLE in CONTAINER) {...} -- but your sample uses if, not for.
side note: it is an error to mutate the collections you iterate over during the iteration.