Julian
Julian

Reputation: 9

NSMutableString to NSMutableArray

I have a NSMutableString with a bunch of floating point numbers.

I need to convert them to NSMutableArray but in groups of 3:

0.015637 0.0205293 0.0270841 0.0157598 0.0202236 0.0272967 0.013217 0.0205293 0.0283439 0.0115028 0.0205293 0.0290539 0.0107803 0.0202236 0.0296187 -0.029892 0.0194995 0.0108798 -0.0299242 0.0191089 0.0108915 0.0243682 0.0194995 0.0204474 0.0307989 0.0205293 -0.00543068 0.0313996 0.0202236 0.00274711 -0.0157598 0.0202236 0.0272967 -0.0180789 0.0202236 0.0258193 -0.0182457 0.0194995 -0.0260576 -0.0182654 0.0191089 -0.0260857 -0.0134582 0.0191089

The numbers are all floating point values and there can be over 30 000 of them or more.

I have tried this but it's not working…

NSArray *contentarray = [content componentsSeparatedByString: @" "];

for(NSNumber * withfastenumeration in contentarray)
{
    [XYZarray addObject: withfastenumeration];
}

Hi Guys,

I wanted to add a bit more information to this question since you guys have been so helpful. Okay so this is a COLLADA XML based project, and it involves vectors so hence the massive array of floating point numbers.

I got to the point where I am parsing with NSXMLParser and isolating the points. They are given however, in a blank format in the XML file as X, Y, Z in repetitive order, only separated by spaces.

I am writing a machining algorithm so I need these numbers in an array, but I need the array to look like:

X0.015637 Y0.0205293 Z0.0270841 X0.0157598 Y0.0202236 Z0.0272967

Once I have that, then I can manipulate all these numbers in a similar fashion.

Again, thanks for these solutions and Happy Holidays!

Upvotes: 1

Views: 838

Answers (3)

Sergey Kuryanov
Sergey Kuryanov

Reputation: 6124

If I understand right you want to receive an array of strings and each string must contain 3 numbers?
Let's use regexp:

NSString *inputString = @"0.015637 0.0205293 0.0270841 0.0157598 0.0202236 0.0272967 0.013217 0.0205293 0.0283439 0.0115028 0.0205293 0.0290539 0.0107803 0.0202236 0.0296187 -0.029892 0.0194995 0.0108798 -0.0299242 0.0191089 0.0108915 0.0243682 0.0194995 0.0204474 0.0307989 0.0205293 -0.00543068 0.0313996 0.0202236 0.00274711 -0.0157598 0.0202236 0.0272967 -0.0180789 0.0202236 0.0258193 -0.0182457 0.0194995 -0.0260576 -0.0182654 0.0191089 -0.0260857 -0.0134582 0.0191089 0.0191089";

NSError *error;
// Creating regexp that will split input string to substring with 3 numbers
NSRegularExpression *regExp = [NSRegularExpression regularExpressionWithPattern:@"(([\\d\\.-]+\\s?){3})"
                                                                        options:0
                                                                          error:&error];

// TODO: Handle error if appear

//
NSArray *matches = [regExp matchesInString:inputString
                                   options:0
                                     range:NSMakeRange(0, [inputString length])];

NSMutableArray *result = [NSMutableArray new];

for (NSTextCheckingResult *match in matches) {
    // Get the matching string
    NSString *substring = [inputString substringWithRange:[match range]];
    // Trim whitespace at the end
    substring = [substring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    [result addObject:substring];
}

Upvotes: 0

holex
holex

Reputation: 24041

1st idea

what about this?

NSMutableString *_string = // your raw string as in your question...
NSLock *_lock = [[NSLock alloc] init];
NSMutableArray *_array = [NSMutableArray array];
[[_string componentsSeparatedByString:@" "] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([_lock tryLock]) [_array addObject:@([obj doubleValue])], [_lock unlock];
}];

2nd idea

that would be a better solution if you want to work with the numbers in group of 3 elegantly:


put these in the .h file:

typedef struct {
    Float64 a;
    Float64 b;
    Float64 c;
} FloatNumbers;

static inline FloatNumbers FloatNumbersMake(Float64 a, Float64 b, Float64 c) {
    FloatNumbers fn; fn.a = a; fn.b = b; fn.c = c; return fn;
}

static inline NSString * NSStringFromFloatNumbers(FloatNumbers fn) {
    return [NSString stringWithFormat:@"{%f, %f, %f}", fn.a, fn.b, fn.c];
}

static inline FloatNumbers FloatNumbersFromString(NSString * string) {
    @try {
        NSArray *_array = [[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"{}"]] componentsSeparatedByString:@", "];
        return FloatNumbersMake([[_array objectAtIndex:0] doubleValue], [[_array objectAtIndex:1] doubleValue], [[_array objectAtIndex:2] doubleValue]);
    }
    @catch (NSException *exception) {
        return FloatNumbersMake(0.f, 0.f, 0.f);
    }
}

and these into any methods of the .m file (where you'd like to parse the raw string):

NSMutableString *_string = // your raw string with the numbers...
NSError *_error;
NSLock *_lock = [[NSLock alloc] init];
NSMutableArray *_mutableArray = [NSMutableArray array];
NSRegularExpression *_regExp = [NSRegularExpression regularExpressionWithPattern:@"(([\\d\\.\\+-]+\\s?){0,3})" options:0 error:&_error];
if (!_error) {
    [_regExp enumerateMatchesInString:_string options:NSMatchingReportProgress range:NSMakeRange(0, _string.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
        FloatNumbers _floatNumbers = FloatNumbersMake(MAXFLOAT, MAXFLOAT, MAXFLOAT);
        @try {
            NSArray *_groupOfNumbers = [[_string substringWithRange:result.range] componentsSeparatedByString:@" "];
            _floatNumbers.a = [[_groupOfNumbers objectAtIndex:0] doubleValue];
            _floatNumbers.b = [[_groupOfNumbers objectAtIndex:1] doubleValue];
            _floatNumbers.c = [[_groupOfNumbers objectAtIndex:2] doubleValue];
        }
        @catch (NSException *exception) {
        }
        @finally {
            if ([_lock tryLock]) [_mutableArray addObject:NSStringFromFloatNumbers(_floatNumbers)], [_lock unlock];
            *stop = (flags == NSMatchingHitEnd);
        }
    }];
} else {
    NSLog(@"%@", _error);
}

the _mutableArray has the numbers, each object is a group of 3; here is an example of how you can read back the desired group of 3 numbers from the array and you are able to work with the values after.

// reading the numbers back
FloatNumbers _secondGroupOf3Numbers = FloatNumbersFromString([_mutableArray objectAtIndex:1]);

NSLog(@"a : %f, b : %f, c : %f", _secondGroupOf3Numbers.a, _secondGroupOf3Numbers.b, _secondGroupOf3Numbers.c);

therefore that will log you the second group of 3 numbers, like:

a : 0.015760, b : 0.020224, c : 0.027297

(bear in mind: those are rounded values for the debug-console only, the float numbers are the same as they were parsed.)

Upvotes: 2

rob mayoff
rob mayoff

Reputation: 386038

componentsSeparatedByString: returns an array of NSString, not an array of NSNumber. You need to write code to convert each component to a number. For example, you can send doubleValue to each component to get back a double, and then use @(...) to wrap the double in an NSNumber.

NSArray *words = [content componentsSeparatedByString:@" "];
NSMutableArray *triples = [NSMutableArray arrayWithCapacity:words.count / 3];
for (NSUInteger i = 0, count = words.count; i < count; i += 3) {
    NSArray *triple = @[
        @([words[i+0] doubleValue]),
        @([words[i+1] doubleValue]),
        @([words[i+2] doubleValue])
    ];
    [triples addObject:triple];
}

Note that -[NSString doubleValue] doesn't do locale-aware parsing. If you need that, you'll need to use an NSScanner or NSNumberFormatter. Also, if you are having trouble with the memory used by the array of 30000+ substrings, use an NSScanner to process the substrings one at a time without creating the big array.

Here's how you do it with NSScanner:

NSScanner *scanner = [NSScanner scannerWithString:content];
// or use localizedScannerWithString: for locale-aware parsing

NSMutableArray *triples = [NSMutableArray arrayWithCapacity:words.count / 3];

double x, y, z;
while ([scanner scanDouble:&x] && [scanner scanDouble:&y] && [scanner scanDouble:&z])  {
    NSArray *triple = @[ @(x), @(y), @(z) ];
    [triples addObject:triple];
}

Upvotes: 3

Related Questions