views:

887

answers:

3

The goal is to generate an NSString chars in length and assign each string to an array. I'm getting stuck on what I need to do with my algorithm to get the correct result. Here's the sample. The result I get is the same randomly generated string added to my array 26 times instead of 26 DIFFERENT strings added.

I've thought about declaring 26 different NSStrings and assigning each result from the algorithm to each string, but that seems inefficient. Thanks for the help.

NSMutableString *string = @"expert";
NSUInteger strLength = [string length];
NSString *letterToAdd;
NSString *finishedWord;
NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];
NSMutableArray *randomArray = [[NSMutableArray alloc] init];

NSArray *charArray = [[NSArray alloc] initWithObjects: @"a", @"b", @"c", @"d", 
                         @"e", @"f", @"g", @"h", @"i", @"j", @"k", @"l", @"m", 
                         @"o", @"p", @"q", @"r", @"s", @"t", @"u", @"v", @"w", 
                         @"x", @"y", @"z", nil];

for (int a = 0; a < 26; a++) {
  for (int i = 0; i < strLength; i++) {

    letterToAdd = [charArray objectAtIndex: arc4random() % [charArray count]];
    if([randomString length] < strLength) {
      [randomString insertString: letterToAdd atIndex: i];
    }

    finishedWord = randomString;
  }

  [randomArray addObject: finishedWord];   
}

NSLog(@"Random Array count %i, contents: %@", [randomArray count], randomArray);
A: 

You're adding the same object to your array every time through the loop, and overwriting it as you go. You mentioned:

I've thought about declaring 26 different NSStrings and assigning each result from the algorithm to each string...

And that is indeed exactly what you need to do. Moving the initialization of randomString into the loop will solve your problem (getting a new NSMutableString on every loop iteration, rather than using a single object). Change the definition of randomString to a simple type definition:

NSMutableString *randomString;

and then in your outer loop, add this line:

randomString = [NSMutableString stringWithCapacity:strLength];

You shouldn't need to change any of the rest of your code.

Carl Norum
+2  A: 

Here's how I would do it:

#import "NSString+Shuffle.h"
NSString * string = @"expert";
NSUInteger strLength = [string length];
NSString * alphabet = @"abcdefghijklmnopqrstuvwxyz";
NSMutableSet * randomWords = [NSMutableSet set];

while ([randomWords count] < 26) {
  NSString * newWord = [alphabet shuffledString];
  newWord = [newWord substringToIndex:strLength];
  [randomArray addObject:newWord];
}
NSLog(@"Random set count %d, contents: %@", [randomWords count], randomWords);

You'd then need a category on NSString that defines shuffledString. This method would simply take the characters in the string and rearrange them randomly. Decent shuffle algorithms can be found quite easily with Google.

I hope you get the basic idea of how this works. The only modification I made is using an NSSet instead of an NSArray, and what the conditional on the loop is. The eliminates the (slim) possibility of duplicate random words.

Edit: since I'm feeling generous, here's a basic shuffledString implementation:

//NSString+Shuffle.h
@interface NSString (ShuffleAdditions)

- (NSString *) shuffledString;

@end

//NSString+Shuffle.m
#import "NSString+Shuffle.h"

@implementation NSString (ShuffleAdditions)

- (NSString *) shuffledString {
  NSMutableString * shuffled = [self mutableCopy];
  NSUInteger length = [shuffled length];
  for (int i = 0; i < (4*length); ++i) {
    NSString * randomChar = [shuffled subStringWithRange:NSMakeRange(arc4random() % (length-1), 1)];
    [shuffled appendString:randomChar];
  }
  return [shuffled autorelease];
}
@end
Dave DeLong
For even faster performance, use a `char*` of the alphabet, and shuffle *that*. Then build an `NSString` using the `char*`
Dave DeLong
That's a lot of indexing into the string to be shuffled, among other things. I think creating a category method on NSString is a little heavyweight for the need. This is a great time to write a plain C function, use one (static) char* to hold the source string, and another to write into, then just create and return an NSString (+stringWithUTF8String:) with the contents of the null-terminated random C string. Quick and easy, and minimizes Objective-C calls for something that doesn't need it, to boot.
Quinn Taylor
Thanks for the different strategy. Very helpful.
samfu_1
+1  A: 

You should create a new randomString each time:

NSMutableString *string = @"expert";
NSUInteger strLength = [string length];
NSString *letterToAdd;
NSString *finishedWord;
//NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];
NSMutableArray *randomArray = [[NSMutableArray alloc] init];

NSArray *charArray = [[NSArray alloc] initWithObjects: @"a", @"b", @"c", @"d", @"e", @"f",
                      @"g", @"h", @"i", @"j", @"k", @"l", @"m", @"o", @"p", @"q", @"r", @"s",
                      @"t", @"u", @"v", @"w", @"x", @"y", @"z", nil];

for (int a = 0; a < 26; a++) {
    NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];

    for (int i = 0; i < strLength; i++) {

            letterToAdd = [charArray objectAtIndex: arc4random() % [charArray count]];
                //if([randomString length] < strLength) {
                    [randomString insertString: letterToAdd atIndex: i];
                //}

        //finishedWord = randomString;
    }

    //[randomArray addObject: finishedWord];
    [randomArray addObject: randomString];
}

NSLog(@"Random Array count %i, contents: %@", [randomArray count], randomArray);
gerry3
worked great, thanks!
samfu_1