What would be the best way to transform NSStrings like these (mixed case)
#"Hello lorem ipsum";
#"i am a test";
to these (camel case without spaces)
#"helloLoremIpsum";
#"iAmATest";
Try this
Using For Loop
- (NSString *)camelCased:(NSString *)aString {
NSMutableString *result = [NSMutableString new];
NSArray *words = [aString componentsSeparatedByString: #" "];
for (NSUInteger i = 0; i < words.count; i++) {
if (i==0) {
[result appendString:([words[i] lowercaseString])];
}
else {
[result appendString:([words[i] capitalizedString])];
}
}
return result;
}
Using Block
- (NSString *)camelCasedUsingBlock:(NSString *)aString {
NSMutableArray *words = [[NSMutableArray alloc] init];
[[aString componentsSeparatedByString:#" "] enumerateObjectsUsingBlock:^(id anObject, NSUInteger idx, BOOL *stop) {
if (idx == 0) {
[words addObject:[anObject lowercaseString]];
}
else{
[words addObject:[anObject capitalizedString]];
}
}];
return [words componentsJoinedByString:#""];
}
NSLog(#"%#",[self camelCased:#"Hello lorem ipsum"]);//helloLoremIpsum
NSLog(#"%#",[self camelCased:#"i am a test"]);//iAmATest
It's overkill but TransformerKit has a bunch of NSValueTransformers that can be used, one is for camel casing. The relevant file is TTTStringTransformers.m if you want to build your own solution for lightness.
Related
I have an array of strings from an online database that I trying to determine the most commonly used word. The values inside the arrays will vary but I want to check the most common words of whatever collection or words I'm using. If theoretically I had an array of the following...
NSArray *stringArray = [NSArray arrayWithObjects:#"Duck", #"Duck", #"Duck", #"Duck", #"Goose"];
How do I iterate through this array to determine the most common string, which would obviously be "Duck"?
Simplest way is probably NSCountedSet:
NSCountedSet* stringSet = [[NSCountedSet alloc] initWithArray:strings];
NSString* mostCommon = nil;
NSUInteger highestCount = 0;
for(NSString* string in stringSet) {
NSUInteger count = [stringSet countForObject:string];
if(count > highestCount) {
highestCount = count;
mostCommon = string;
}
}
You can use the word as a key into a dictionary.
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
}
Now iterate through words and find the key with the highest value.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
for (NSString *key in [words allKeys]) {
if ([words[key] compare:curMax] == NSOrderedDescending) {
mostCommon = key;
curMax = word[key];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
EDIT: Rather than looping through the array once then looping separately through the sorted dictionary, I think we can do better and do it all in a single loop.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
if ([words[word] compare:curMax] == NSOrderedDescending) {
mostCommon = word;
curMax = words[word];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
This should be significantly faster than my answer pre-edit, though I don't know how it compares to using the NSCountedSet answer.
Try using NSPredicate.
NSUInteger count=0;
NSString *mostCommonStr;
for(NSString *strValue in stringArray) {
NSUInteger countStr=[[stringArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self MATCHES[CD] %#, strValue]]count];
if(countStr > count) {
count=countStr;
mostCommonStr=strValue;
}
}
NSLog(#"The most commonstr is %#",mostCommonStr);
I want to make a method which gives reverse of string.suppose I pass a NSString "Welcome to Objective C" in method and that method return a reverse of string like "C Objective to Welcome" not "C evitcejbO ot emocleW" without the use of componentsSeparatedByString method.
Is it possible to do with Objective c..?
Please help.
You can enumerate strings by words.
NSString *string = #"Welcome to Objective-C!";
NSMutableArray *words = [NSMutableArray array];
[string enumerateLinguisticTagsInRange:NSMakeRange(0, [string length])
scheme:NSLinguisticTagSchemeTokenType
options:0
orthography:nil
usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
[array addObject:[string substringWithRange:tokenRange]];
}];
NSMutableString *reverseString = [[NSMutableString alloc] init];
for (NSString *word in [words reverseObjectEnumerator]){
[reverse appendString:word];
}
NSLog(#"%#", reverseString);
This will print...
"!C-Objective to Welcome"
You can change the options to omit whitespaces and stuff...
I used below method for reversing string in iOS
- (NSString *)reverseString:(NSString *)stringToReverse
{
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[stringToReverse length]];
[stringToReverse enumerateSubstringsInRange:NSMakeRange(0, [stringToReverse length])
options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return reversedString;
}
Sorry I misread your question earlier. I did it using a series of loops, my answer is messier than Fogmeister but I wanted to give it a shot to see if I could do it.
NSString *str = #"This is a test";
NSMutableArray *array = [[NSMutableArray alloc] init];
for(int i = 0; i < [str length]; i++)
{
char sTest = [str characterAtIndex:i];
if(sTest == ' ')
{
[array addObject:[NSNumber numberWithInt:i]];
}
}
NSInteger iNext = [[array objectAtIndex:[array count]-1] integerValue];
iNext+=1;
if(iNext < [str length])
{
[array addObject:[NSNumber numberWithInt:iNext]];
}
NSMutableArray *wordArray = [[NSMutableArray alloc] init];
for(int i = 0; i < [array count]; i++)
{
if (i == 0)
{
int num = [[array objectAtIndex:i] integerValue];
NSString *s = [[str substringFromIndex:0] substringToIndex:num];
[wordArray addObject:s];
}
else if(i == [array count]-1)
{
int prev = [[array objectAtIndex:i-1] integerValue]+1;
int num = [str length];
NSString *s = [[str substringToIndex:num] substringFromIndex:prev];
[wordArray addObject:s];
}
else
{
int prev = [[array objectAtIndex:i-1] integerValue]+1;
int num = [[array objectAtIndex:i] integerValue];
NSString *s = [[str substringToIndex:num] substringFromIndex:prev];
[wordArray addObject:s];
}
}
NSMutableArray *reverseArray = [[NSMutableArray alloc]init];
for(int i = [wordArray count]-1; i >= 0; i--)
{
[reverseArray addObject:[wordArray objectAtIndex:i]];
}
NSLog(#"%#", reverseArray);
Here i have done with replacing character with minimal number of looping. log(n/2).
NSString *string=#"Happy World";
NSInteger lenth=[string length];
NSInteger halfLength=[string length]/2;
for(int i=0;i<halfLength;i++)
{
NSString *leftString=[NSString stringWithFormat:#"%c",[string characterAtIndex:i]];
NSString *rightString=[NSString stringWithFormat:#"%c",[string characterAtIndex:(lenth-i-1)]];
string= [string stringByReplacingCharactersInRange:NSMakeRange(i, 1) withString:rightString];
string=[string stringByReplacingCharactersInRange:NSMakeRange((lenth-i-1), 1) withString:leftString];
}
NSLog(#"%#",string);
Try This , It's working perfect as per your expectation ,
Call Function :-
[self reversedString:#"iOS"];
Revers String Function :-
-(void)reversedString :(NSString *)reversStr
{ // reversStr is "iOS"
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [reversStr length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[reversStr substringWithRange:subStrRange]];
}
NSLog(#"%#", reversedString); // outputs "SOi"
}
Hope So this is help for some one .
There is no API to do that, if that's what you are asking.
You can always iterate through the string looking for white spaces (or punctuation, it depends on your needs), identify the words and recompose your "reversed" message manually.
I'm trying to re-arrange words into alphabetical order. For example, tomato would become amoott, or stack would become ackst.
I've found some methods to do this in C with char arrays, but I'm having issues getting that to work within the confines of the NSString object.
Is there an easier way to do it within the NSString object itself?
You could store each of the string's characters into an NSArray of NSNumber objects and then sort that. Seems a bit expensive, so I would perhaps just use qsort() instead.
Here it's provided as an Objective-C category (untested):
NSString+SortExtension.h:
#import <Foundation/Foundation.h>
#interface NSString (SortExtension)
- (NSString *)sorted;
#end
NSString+SortExtension.m:
#import "NSString+SortExtension.h"
#implementation NSString (SortExtension)
- (NSString *)sorted
{
// init
NSUInteger length = [self length];
unichar *chars = (unichar *)malloc(sizeof(unichar) * length);
// extract
[self getCharacters:chars range:NSMakeRange(0, length)];
// sort (for western alphabets only)
qsort_b(chars, length, sizeof(unichar), ^(const void *l, const void *r) {
unichar left = *(unichar *)l;
unichar right = *(unichar *)r;
return (int)(left - right);
});
// recreate
NSString *sorted = [NSString stringWithCharacters:chars length:length];
// clean-up
free(chars);
return sorted;
}
#end
I think separate the string to an array of string(each string in the array contains only one char from the original string). Then sort the array will be OK. This is not efficient but is enough when the string is not very long. I've tested the code.
NSString *str = #"stack";
NSMutableArray *charArray = [NSMutableArray arrayWithCapacity:str.length];
for (int i=0; i<str.length; ++i) {
NSString *charStr = [str substringWithRange:NSMakeRange(i, 1)];
[charArray addObject:charStr];
}
NSString *sortedStr = [[charArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] componentsJoinedByString:#""];
// --------- Function To Make an Array from String
NSArray *makeArrayFromString(NSString *my_string) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < my_string.length; i ++) {
[array addObject:[NSString stringWithFormat:#"%c", [my_string characterAtIndex:i]]];
}
return array;
}
// --------- Function To Sort Array
NSArray *sortArrayAlphabetically(NSArray *my_array) {
my_array= [my_array sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
return my_array;
}
// --------- Function Combine Array To Single String
NSString *combineArrayIntoString(NSArray *my_array) {
NSString * combinedString = [[my_array valueForKey:#"description"] componentsJoinedByString:#""];
return combinedString;
}
// Now you can call the functions as in below where string_to_arrange is your string
NSArray *blowUpArray;
blowUpArray = makeArrayFromString(string_to_arrange);
blowUpArray = sortArrayAlphabetically(blowUpArray);
NSString *arrayToString= combineArrayIntoString(blowUpArray);
NSLog(#"arranged string = %#",arrayToString);
Just another example using NSMutableString and sortUsingComparator:
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:#"tomat"];
[mutableString appendString:#"o"];
NSLog(#"Orignal string: %#", mutableString);
NSMutableArray *charArray = [NSMutableArray array];
for (int i = 0; i < mutableString.length; ++i) {
[charArray addObject:[NSNumber numberWithChar:[mutableString characterAtIndex:i]]];
}
[charArray sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
if ([obj1 charValue] < [obj2 charValue]) return NSOrderedAscending;
return NSOrderedDescending;
}];
[mutableString setString:#""];
for (int i = 0; i < charArray.count; ++i) {
[mutableString appendFormat:#"%c", [charArray[i] charValue]];
}
NSLog(#"Sorted string: %#", mutableString);
Output:
Orignal string: tomato
Sorted string: amoott
How can I optimise out this nested for loop?
The program should go through each word in the array created from the word text file, and if it's greater than 8 characters, add it to the goodWords array. But the caveat is that I only want the root word to be in the goodWords array, for example:
If greet is added to the array, I don't want greets or greetings or greeters, etc.
NSString *string = [NSString stringWithContentsOfFile:#"/Users/james/dev/WordParser/word.txt" encoding:NSUTF8StringEncoding error:NULL];
NSArray *words = [string componentsSeparatedByString:#"\r\n"];
NSMutableArray *goodWords = [NSMutableArray array];
BOOL shouldAddToGoodWords = YES;
for (NSString *word in words)
{
NSLog(#"Word: %#", word);
if ([word length] > 8)
{
NSLog(#"Word is greater than 8");
for (NSString *existingWord in [goodWords reverseObjectEnumerator])
{
NSLog(#"Existing Word: %#", existingWord);
if ([word rangeOfString:existingWord].location != NSNotFound)
{
NSLog(#"Not adding...");
shouldAddToGoodWords = NO;
break;
}
}
if (shouldAddToGoodWords)
{
NSLog(#"Adding word: %#", word);
[goodWords addObject:word];
}
}
shouldAddToGoodWords = YES;
}
How about something like this?
//load the words from wherever
NSString * allWords = [NSString stringWithContentsOfFile:#"/usr/share/dict/words"];
//create a mutable array of the words
NSMutableArray * words = [[allWords componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy];
//remove any words that are shorter than 8 characters
[words filterUsingPredicate:[NSPredicate predicateWithFormat:#"length >= 8"]];
//sort the words in ascending order
[words sortUsingSelector:#selector(caseInsensitiveCompare:)];
//create a set of indexes (these will be the non-root words)
NSMutableIndexSet * badIndexes = [NSMutableIndexSet indexSet];
//remember our current root word
NSString * currentRoot = nil;
NSUInteger count = [words count];
//loop through the words
for (NSUInteger i = 0; i < count; ++i) {
NSString * word = [words objectAtIndex:i];
if (currentRoot == nil) {
//base case
currentRoot = word;
} else if ([word hasPrefix:currentRoot]) {
//word is a non-root word. remember this index to remove it later
[badIndexes addIndex:i];
} else {
//no match. this word is our new root
currentRoot = word;
}
}
//remove the non-root words
[words removeObjectsAtIndexes:badIndexes];
NSLog(#"%#", words);
[words release];
This runs very very quickly on my machine (2.8GHz MBP).
A Trie seems suitable for your purpose. It is like a hash, and is useful for detecting if a given string is a prefix of an already seen string.
I used an NSSet to ensure that you only have 1 copy of a word added at a time. It will add a word if the NSSet does not already contain it. It then checks to see if the new word is a substring for any word that has already been added, if true then it won't add the new word. It's case-insensitive as well.
What I've written is a refactoring of your code. It's probably not that much faster but you really do want a tree data structure if you want to make it a lot faster when you want to search for words that have already been added to your tree.
Take a look at RedBlack Trees or B-Trees.
Words.txt
objective
objectively
cappucin
cappucino
cappucine
programme
programmer
programmatic
programmatically
Source Code
- (void)addRootWords {
NSString *textFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"txt"];
NSString *string = [NSString stringWithContentsOfFile:textFile encoding:NSUTF8StringEncoding error:NULL];
NSArray *wordFile = [string componentsSeparatedByString:#"\n"];
NSMutableSet *goodWords = [[NSMutableSet alloc] init];
for (NSString *newWord in wordFile)
{
NSLog(#"Word: %#", newWord);
if ([newWord length] > 8)
{
NSLog(#"Word '%#' contains 8 or more characters", newWord);
BOOL shouldAddWord = NO;
if ( [goodWords containsObject:newWord] == NO) {
shouldAddWord = YES;
}
for (NSString *existingWord in goodWords)
{
NSRange textRange = [[newWord lowercaseString] rangeOfString:[existingWord lowercaseString]];
if( textRange.location != NSNotFound ) {
// newWord contains the a substring of existingWord
shouldAddWord = NO;
break;
}
NSLog(#"(word:%#) does not contain (substring:%#)", newWord, existingWord);
shouldAddWord = YES;
}
if (shouldAddWord) {
NSLog(#"Adding word: %#", newWord);
[goodWords addObject:newWord];
}
}
}
NSLog(#"***Added words***");
int count = 1;
for (NSString *word in goodWords) {
NSLog(#"%d: %#", count, word);
count++;
}
[goodWords release];
}
Output:
***Added words***
1: cappucino
2: programme
3: objective
4: programmatic
5: cappucine
I'm looking for a simple, efficient way to convert strings in CamelCase to underscore notation (i.e., MyClassName -> my_class_name) and back again in Objective C.
My current solution involves lots of rangeOfString, characterAtIndex, and replaceCharactersInRange operations on NSMutableStrings, and is just plain ugly as hell :) It seems that there must be a better solution, but I'm not sure what it is.
I'd rather not import a regex library just for this one use case, though that is an option if all else fails.
Chris's suggestion of RegexKitLite is good. It's an excellent toolkit, but this could be done pretty easily with NSScanner. Use -scanCharactersFromSet:intoString: alternating between +uppercaseLetterCharacterSet and +lowercaseLetterCharacterSet. For going back, you'd use -scanUpToCharactersFromSet: instead, using a character set with just an underscore in it.
How about these:
NSString *MyCamelCaseToUnderscores(NSString *input) {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [input length]; idx += 1) {
unichar c = [input characterAtIndex:idx];
if ([uppercase characterIsMember:c]) {
[output appendFormat:#"_%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
NSString *MyUnderscoresToCamelCase(NSString *underscores) {
NSMutableString *output = [NSMutableString string];
BOOL makeNextCharacterUpperCase = NO;
for (NSInteger idx = 0; idx < [underscores length]; idx += 1) {
unichar c = [underscores characterAtIndex:idx];
if (c == '_') {
makeNextCharacterUpperCase = YES;
} else if (makeNextCharacterUpperCase) {
[output appendString:[[NSString stringWithCharacters:&c length:1] uppercaseString]];
makeNextCharacterUpperCase = NO;
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
Some drawbacks are that they do use temporary strings to convert between upper and lower case, and they don't have any logic for acronyms, so myURL will result in my_u_r_l.
Try this magic:
NSString* camelCaseString = #"myBundleVersion";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])" options:0 error:nil];
NSString *underscoreString = [[regex stringByReplacingMatchesInString:camelCaseString options:0 range:NSMakeRange(0, camelCaseString.length) withTemplate:#"_$1$2"] lowercaseString];
NSLog(#"%#", underscoreString);
Output: my_bundle_version
If your concern is just the visibility of your code, you could make a category for NSString using the methods you've designed already. That way, you only see the ugly mess once. ;)
For instance:
#interface NSString(Conversions) {
- (NSString *)asCamelCase;
- (NSString *)asUnderscored;
}
#implementation NSString(Conversions) {
- (NSString *)asCamelCase {
// whatever you came up with
}
- (NSString *)asUnderscored {
// whatever you came up with
}
}
EDIT: After a quick Google search, I couldn't find any way of doing this, even in plain C. However, I did find a framework that could be useful. It's called RegexKitLite. It uses the built-in ICU library, so it only adds about 20K to the final binary.
Here's my implementation of Rob's answer:
#implementation NSString (CamelCaseConversion)
// Convert a camel case string into a dased word sparated string.
// In case of scanning error, return nil.
// Camel case string must not start with a capital.
- (NSString *)fromCamelCaseToDashed {
NSScanner *scanner = [NSScanner scannerWithString:self];
scanner.caseSensitive = YES;
NSString *builder = [NSString string];
NSString *buffer = nil;
NSUInteger lastScanLocation = 0;
while ([scanner isAtEnd] == NO) {
if ([scanner scanCharactersFromSet:[NSCharacterSet lowercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:buffer];
if ([scanner scanCharactersFromSet:[NSCharacterSet uppercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:#"-"];
builder = [builder stringByAppendingString:[buffer lowercaseString]];
}
}
// If the scanner location has not moved, there's a problem somewhere.
if (lastScanLocation == scanner.scanLocation) return nil;
lastScanLocation = scanner.scanLocation;
}
return builder;
}
#end
Here's yet another version based on all the above. This version handles additional forms. In particular, tested with the following:
camelCase => camel_case
camelCaseWord => camel_case_word
camelURL => camel_url
camelURLCase => camel_url_case
CamelCase => camel_case
Here goes
- (NSString *)fromCamelCaseToDashed3 {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
BOOL previousCharacterWasUppercase = FALSE;
BOOL currentCharacterIsUppercase = FALSE;
unichar currentChar = 0;
unichar previousChar = 0;
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
previousChar = currentChar;
currentChar = [self characterAtIndex:idx];
previousCharacterWasUppercase = currentCharacterIsUppercase;
currentCharacterIsUppercase = [uppercase characterIsMember:currentChar];
if (!previousCharacterWasUppercase && currentCharacterIsUppercase && idx > 0) {
// insert an _ between the characters
[output appendString:#"_"];
} else if (previousCharacterWasUppercase && !currentCharacterIsUppercase) {
// insert an _ before the previous character
// insert an _ before the last character in the string
if ([output length] > 1) {
unichar charTwoBack = [output characterAtIndex:[output length]-2];
if (charTwoBack != '_') {
[output insertString:#"_" atIndex:[output length]-1];
}
}
}
// Append the current character lowercase
[output appendString:[[NSString stringWithCharacters:¤tChar length:1] lowercaseString]];
}
return output;
}
If you are concerned with the speed of your code you probably want to write a more performant version of the code:
- (nonnull NSString *)camelCaseToSnakeCaseString {
if ([self length] == 0) {
return #"";
}
NSMutableString *output = [NSMutableString string];
NSCharacterSet *digitSet = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *uppercaseSet = [NSCharacterSet uppercaseLetterCharacterSet];
NSCharacterSet *lowercaseSet = [NSCharacterSet lowercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
unichar c = [self characterAtIndex:idx];
// if it's the last one then just append lowercase of character
if (idx == [self length] - 1) {
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
continue;
}
unichar nextC = [self characterAtIndex:(idx+1)];
// this logic finds the boundaries between lowercase/uppercase/digits and lets the string be split accordingly.
if ([lowercaseSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([lowercaseSet characterIsMember:c] && [digitSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([digitSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
// Append lowercase of character
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
}
}
return output;
}
I have combined the answers found here into my refactoring library, es_ios_utils. See NSCategories.h:
#property(nonatomic, readonly) NSString *asCamelCaseFromUnderscores;
#property(nonatomic, readonly) NSString *asUnderscoresFromCamelCase;
Usage:
#"my_string".asCamelCaseFromUnderscores
yields #"myString"
Please push improvements!
I happened upon this question looking for a way to convert Camel Case to a spaced, user displayable string. Here is my solution which worked better than replacing #"_" with #" "
- (NSString *)fromCamelCaseToSpaced:(NSString*)input {
NSCharacterSet* lower = [NSCharacterSet lowercaseLetterCharacterSet];
NSCharacterSet* upper = [NSCharacterSet uppercaseLetterCharacterSet];
for (int i = 1; i < input.length; i++) {
if ([upper characterIsMember:[input characterAtIndex:i]] &&
[lower characterIsMember:[input characterAtIndex:i-1]])
{
NSString* soFar = [input substringToIndex:i];
NSString* left = [input substringFromIndex:i];
return [NSString stringWithFormat:#"%# %#", soFar, [self fromCamelCaseToSpaced:left]];
}
}
return input;
}
OK guys. Here is an all regex answer, which I consider the only true way:
Given:
NSString *MYSTRING = "foo_bar";
NSRegularExpression *_toCamelCase = [NSRegularExpression
regularExpressionWithPattern:#"(_)([a-z])"
options:NSRegularExpressionCaseInsensitive error:&error];
NSString *camelCaseAttribute = [_toCamelCase
stringByReplacingMatchesInString:MYSTRING options:0
range:NSMakeRange(0, attribute.length)
withTemplate:#"\\U$2"];
Yields fooBar.
Conversely:
NSString *MYSTRING = "fooBar";
NSRegularExpression *camelCaseTo_ = [NSRegularExpression
regularExpressionWithPattern:#"([A-Z])"
options:0 error:&error];
NSString *underscoreParsedAttribute = [camelCaseTo_
stringByReplacingMatchesInString:MYSTRING
options:0 range:NSMakeRange(0, attribute.length)
withTemplate:#"_$1"];
underscoreParsedAttribute = [underscoreParsedAttribute lowercaseString];
Yields: foo_bar.
\U$2 replaces second capture group with upper-case version of itself :D
\L$1 however, oddly, does not replace the first capture group with a lower-case version of itself :( Not sure why, it should work. :/