HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Bats Challenge from CodeEval.com

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
codeevalchallengecomfrombats

Problem

I am going through coding challenges and I would like to get feedback on one of them.
I am not sure if i can post the link to the challenge but here it is. CodeEval Challenge


Outside of your window, there's a wire between two
buildings. Bats love to hang there but you notice they never hang
closer than "d" centimeters from each other. They also don't hang
closer than 6 centimeters from any of the buildings.


Your goal is to determine the maximum number of additional bats that
can fit on that wire assuming they have zero width.


INPUT SAMPLE:


Each line of input contains three space separated integers: the length of
the wire "l", distance "d" and number of bats "n" already hanging on
the wire.


"n" numbers contain the positions of the bats in any order. All number
are integers. You can assume that the bats already hanging on the wire
are at least 6 cm from the poles and at least "d" centimeters apart
from each other.:


22 2 2 9 11

33 5 0

16 3 2 6 10

835 125 1 113

47 5 0


Output

3

5

0

5

8

Here is my solution. I commented it a bit. I had to convert the method to C to get it passed through the automated system. Any suggestions to improve it would be greatly appreciated.

```
#define CENTIMETERS_FROM_WALL 6

int howManyBatsBetween(int pointOne, int pointTwo, int distanceBetween)
{
//calculates how many bats you can put between two bats already on the wire
//Have to make sure that it would be proper distance between the first bat and second bat
int nextBatPosition = pointOne+distanceBetween;
int batCount = 0;
while ((nextBatPosition+distanceBetween) <= pointTwo) {
batCount++;
nextBatPosition+=distanceBetween;
}
return batCount;
}

-(void)batCount
{
NSString *line = @"47 5 0"; //input
//extract input
NSArray *inputArray = [line componentsSeparatedByString:@" "];
int lengthWire = [[inputArray objectAtIndex:0] intValue];
int d

Solution

You posted this question with the objective-c tag, and made this comment:


I had to convert the method to C to get it passed through the automated system.

As such, I'm going to assume you're perhaps more interested in a review of the Objective-C stuff, so that's what this'll be. (Essentially, I'm ignoring the howManyBatsBetween function for this.)

As a start, the first problem I notice is that our method takes no arguments and returns nothing. The problem statement gives sample inputs in the form of strings and expects an integer return. Our method should be changed to reflect this:

- (NSInteger)batsOnWire:(NSString *)wire;


Now we can remove the hardcoded // input.

componentsSeparatedByString: is a good approach at breaking up the string, but we also need to make sure our input is in a good format.

NSArray *batPositions = [wire componentsSeparatedByString:@" "];

if (batPositions.count < 3) {
    return NSNotFound;
}


We'll use NSNotFound as a return result representing some sort of problem with the input. Truly we should actually take an NSError ** as an argument, but I won't get into that mess. I'll keep it simple for now. The point here is, we're dealing with inputs, either typed by a user or read from a file... and inputs always have a chance of having errors, so we have to deal with that.

Grabbing lengthWire, distanceFromEachOther, and batsOnWire as you do is fine.

But again, we are dealing with user input. The problem with calling intValue is that if the string in question can't be parsed as an int, the method just returns 0. It's impossible to tell the difference between @"0" and @"Hello World" when looking at what they return intValue.

[@"0" intValue] == [@"Hello World" intValue]


Instead, we can convert these strings to NSNumber objects... which we'll want to do later for another reason which I'll address, but here's how we do it:

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;

NSNumber lengthWire = [formatter numberFromString: batPositions[0]];
if (!lengthWire) {
    return NSNotFound;
}


NSNumberFormatter's numberFromString: method returns nil if the string cannot be parsed to a number. Otherwise, it returns an NSNumber object representing the value. Once we do the if(!lengthWire) check, we know we have a valid number. We can always extract the raw int value by calling the same intValue on this NSNumber object.

But there's another reason we're going to want the entire array of strings converted into an array of NSNumber objects.

Given the following mutable array of strings:

@[@"1", @"2", @"40", @"10", @"300", @"100", @"9"];


If we sort if using sortedArrayUsingSelector:@selector(compare:)];, the result would be an array of this order:

@[@"1", @"10", @"100", @"2", @"300", @"40", @"9"];


And this isn't the right order. The compare: method of NSString compares the strings character by character.

If we have to strings we want to compare individually, we can call another method:

[@"2" compare:@"100" options:NSNumericSearch];


And we'll get the correct numeric result (2 would be sorted before 100 correctly).

But when using sortedArrayUsingSelector:, we can't use a method that takes other arguments. We can't specify that our strings our compared numerically.

However, if they are NSNumber objects, they'll of course be compared as numbers and sorted appropriately. So if we turn our array of strings into an array of NSNumber objects, and then call the same:

[arrayOfNumberObjects sortedArrayUsingSelector:@selector(compare:)];


We'll get our array sorted in the proper order.

And once again, NSNumber responds to the intValue method in exactly the same way NSString does, so we can still use this to get a workable int out of the object.

#define CENTIMETERS_FROM_WALL 6


As one final note about Objective-C, we don't really care for #define variables. I can't discourage it enough. In fact, Apple agrees with me. As evidence, you can't even use the #define functionality in their new language, Swift, at all.

Instead, we should opt for a typed constant:

static const int kCentimetersFromWall = 6;

Code Snippets

- (NSInteger)batsOnWire:(NSString *)wire;
NSArray *batPositions = [wire componentsSeparatedByString:@" "];

if (batPositions.count < 3) {
    return NSNotFound;
}
[@"0" intValue] == [@"Hello World" intValue]
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;

NSNumber lengthWire = [formatter numberFromString: batPositions[0]];
if (!lengthWire) {
    return NSNotFound;
}
@[@"1", @"2", @"40", @"10", @"300", @"100", @"9"];

Context

StackExchange Code Review Q#68877, answer score: 5

Revisions (0)

No revisions yet.