How can I use NSRange with integers to simplify my code?
Asked Answered
C

4

6

I've just started learning Objective-C and made a little compass app that will display a direction when it falls into a range of headings. It works just fine, but I wonder if there is a more concise way of writing it using NSRange. After a lot of looking, it seems like NSRange is used more for string functions rather than numbers.

I tried to make an instance of NSRange my starting point to make this more concise, I couldn't track down the function that would find if a number falls within an NSRange.

Am I on the right track here, or am I making this more verbose than it needs to be?

Thanks in advance..

Here was my failed jumping off point for attempting to shorten up the code:

// If heading falls within this range, then display "S" for south    
NSRange eastenRange = NSMakeRange (80, 100); 
NSRange southernRange = NSMakeRange (170, 190); 
etc...

Here is my current code (works fine):

- (void)locationManager:(CLLocationManager *)manager 
    didUpdateHeading:(CLHeading *)newHeading
{
 // Define and display the heading
 NSNumber *theHeading = [NSNumber numberWithInt:[newHeading trueHeading]];
 [headingLabel setText:[NSString stringWithFormat:@"%@°", theHeading]];

 // Define the range of directions
 NSNumber *northLowerRange = [NSNumber numberWithInt:10];
 NSNumber *northUpperRange = [NSNumber numberWithInt:350];

 NSNumber *eastLowerRange = [NSNumber numberWithInt:80];
 NSNumber *eastUpperRange = [NSNumber numberWithInt:100];

 NSNumber *southLowerRange = [NSNumber numberWithInt:170];
 NSNumber *southUpperRange = [NSNumber numberWithInt:190];

 NSNumber *westLowerRange = [NSNumber numberWithInt:260];
 NSNumber *westUpperRange = [NSNumber numberWithInt:280];


 // If the heading falls within the correct ranges, then display the direction
 if ([northLowerRange compare:theHeading] == NSOrderedDescending || [northUpperRange compare:theHeading] == NSOrderedAscending)
  [directionLabel setText:@"N"];
 else if ([eastLowerRange compare:theHeading] == NSOrderedAscending && [eastUpperRange compare:theHeading] == NSOrderedDescending)
  [directionLabel setText:@"E"];
 else if ([southLowerRange compare:theHeading] == NSOrderedAscending && [southUpperRange compare:theHeading] == NSOrderedDescending)
  [directionLabel setText:@"S"];
 else if ([westLowerRange compare:theHeading] == NSOrderedAscending && [westUpperRange compare:theHeading] == NSOrderedDescending)
  [directionLabel setText:@"W"];
 else
  [directionLabel setText:@"-"];

}
Coagulum answered 9/11, 2010 at 21:56 Comment(0)
U
3

am I making this more verbose than it needs to be?

Yes. When you want to do numerical operations, avoid NSNumber. The NSNumber class exists only because Objective-C collections like NSArray, NSDictionary etc. can only hold Objective-C objects. Otherwise, you should always use plain int or NSInteger or CGFloat or double etc.

 int heading = [newHeading trueHeading];
 headingLabel.text = [NSString stringWithFormat:@"%d°", heading];

 if (10 < heading || heading > 350)
    directionLabel.text = @"N";
 else if (80 < heading && heading < 100)
    directionLabel.text = @"E";
 // and so on.

You don't need to use NSRange.

Unsecured answered 9/11, 2010 at 22:2 Comment(4)
It's generally better in Cocoa and Cocoa Touch to use the architecture-independent NSInteger and NSUInteger rather than int.Personate
@Chuck: Right. But OP uses +numberWithInt: so I keep the int in the code.Unsecured
Thanks, @KennyTM. Much cleaner now. @Personate - took your tip on NSUInteger and used that too. I initially used numberWithInt: because it didn't throw an exception, so I kept it.Coagulum
@Personate NSDecimalNumber is also useful when working with numeric values that are sensitive to decimal values, as when working with currency. Checkout this: https://mcmap.net/q/501093/-should-i-use-nsdecimalnumber-to-deal-with-moneyTownley
S
10

Coming late to the party but the following would work and make use of ranges I believe:

NSRange easternRange = NSMakeRange (80, 20); 
NSRange southernRange = NSMakeRange (170, 20); 

NSInteger heading = 92;
if (NSLocationInRange(heading,easternRange)) {
  NSLog(@"Heading Easterly.");
} else if (NSLocationInRange(heading,southernRange)) {
  NSLog(@"Heading southerly.");
}

etc. etc.

Strew answered 10/1, 2013 at 15:42 Comment(0)
U
3

am I making this more verbose than it needs to be?

Yes. When you want to do numerical operations, avoid NSNumber. The NSNumber class exists only because Objective-C collections like NSArray, NSDictionary etc. can only hold Objective-C objects. Otherwise, you should always use plain int or NSInteger or CGFloat or double etc.

 int heading = [newHeading trueHeading];
 headingLabel.text = [NSString stringWithFormat:@"%d°", heading];

 if (10 < heading || heading > 350)
    directionLabel.text = @"N";
 else if (80 < heading && heading < 100)
    directionLabel.text = @"E";
 // and so on.

You don't need to use NSRange.

Unsecured answered 9/11, 2010 at 22:2 Comment(4)
It's generally better in Cocoa and Cocoa Touch to use the architecture-independent NSInteger and NSUInteger rather than int.Personate
@Chuck: Right. But OP uses +numberWithInt: so I keep the int in the code.Unsecured
Thanks, @KennyTM. Much cleaner now. @Personate - took your tip on NSUInteger and used that too. I initially used numberWithInt: because it didn't throw an exception, so I kept it.Coagulum
@Personate NSDecimalNumber is also useful when working with numeric values that are sensitive to decimal values, as when working with currency. Checkout this: https://mcmap.net/q/501093/-should-i-use-nsdecimalnumber-to-deal-with-moneyTownley
S
0

A range takes a location and a length. So if you want 80 to 100 degrees for an eastern range, you could use NSMakeRange(80, 20). That would create a range starting at 80 degrees, spanning 20 degrees.

Semitone answered 9/11, 2010 at 22:1 Comment(0)
E
0

If you are wanting a more integrated use of the NSRange struct, I have found it useful for comparing portions of arrays:

NSRange aRange = NSRangeFromString([NSString stringWithFormat:@"{0:%d}",items.count]);
NSIndexSet* aSet = [NSIndexSet indexSetWithIndexesInRange:aRange];
NSIndexSet *hasNotBeenReadSet = [items indexesOfObjectsAtIndexes:aSet
                                                          options:NSEnumerationConcurrent
                                                      passingTest:
                                  ^BOOL(BasicItem* obj, NSUInteger idx, BOOL *stop) {
                                    return [obj hasNotBeenRead];
                                  }];

int numberOfUnreadItems = hasNotBeenReadSet.count;
Engineman answered 4/2, 2014 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.