get the last whitespace from nsstring in objective c - ios

I have a large string set something like - Objective-C is the primary programming language you use when writing software for OS X and iOS. It’s a superset of the C programming language and provides object-oriented capabilities and a dynamic runtime. Objective-C inherits the syntax, primitive types, and flow control statements of C and adds syntax for defining classes and methods. It also adds language-level support for object graph management and object literals while providing dynamic typing and binding, deferring many responsibilities until runtime.
I wants to divide above string in the length of at least 15 characters strings which will be ending by whitespaces & store in an array. For this I have applied following logic which is working fine , But it will be busy the compiler in case of long strings like 1000000 characters , Please help me if you can suggest better way to meet by requirements .Thanks.
NSMutableArray *randomSelection = [[NSMutableArray alloc] init];
NSString *shortDescription = shortDesc_;
NSString *str=#"";
NSArray *iarray=[shortDescription componentsSeparatedByString:#" "];
NSInteger i=0;
while (i<iarray.count)
{
NSString *prevStr=str;
str=[prevStr stringByAppendingString:[[iarray objectAtIndex:i] stringByAppendingString:#" "]];
if(str.length>15)
{
[randomSelection addObject:prevStr];
str=[[iarray objectAtIndex:i] stringByAppendingString:#" "];
}
i++;
}
if(str.length>0)
[randomSelection addObject:str];

Let's consider your algorithm, which as you correctly state works.
You first break up your million characters into "words", so you have another million characters in memory at this point, and then you join the words into strings of 15 or more characters - and that will occupy another million or so characters of memory. Maybe you could remove the need for the intermediate million by breaking up the input directly at the right points? That should reduce your memory requirements by roughly 1/3 and save a fair bit of copying.
Next when building up your string of 15 or so characters you repeatedly call length to calculate the length of the newly extended string. However you know how long it is - the previous length + length of added word + 1 for the space. Maybe keeping track of the length would save worthwhile time?
But then you wouldn't need to keep track of the length at all if you broke the string in the right place, but is that possible?
Consider the methods:
rangeOfCharacterFromSet:options:range or rangeOfString:options:range; and
substringWithRange:
One of the first two can be used to easily find the range of characters which make up your next string of 15 or so, and the last enables you to extract it. No intermediate million or so characters in memory, no need to keep checking the length.
Will it be faster? Give it a go and find out!
HTH

Try this
int numberOfLetters = 15;
if ([text length]>numberOfLetters) {
NSMutableArray* strings = [[NSMutableArray alloc] init];
__block int checkedStringLenght = 0;
while (checkedStringLenght<text.length-1) {
int rest = (int)text.length - checkedStringLenght;
__block NSRange neededRange;
[text enumerateSubstringsInRange:NSMakeRange(checkedStringLenght, MIN(rest,numberOfLetters)) options:NSStringEnumerationByWords | NSStringEnumerationReverse usingBlock:^(NSString *substring, NSRange subrange, NSRange enclosingRange, BOOL *stop) {
unsigned long fin =subrange.location - checkedStringLenght-1;
if (fin == 0){
NSString* lastString = [text substringWithRange:NSMakeRange(checkedStringLenght, rest)];
if ([[strings lastObject] length] + rest <= numberOfLetters) {
[strings replaceObjectAtIndex:strings.count-1 withObject:[strings[strings.count-1] stringByAppendingString:lastString]];
} else [strings addObject:lastString];
checkedStringLenght+=rest;
}
else {
neededRange =NSMakeRange(checkedStringLenght, fin);
NSString *ch = [text substringWithRange:neededRange];
checkedStringLenght+=ch.length;
[strings addObject:ch];
}
*stop = YES;
}];
}
NSLog(#"%#",strings);
}
My while is executing only (int)(text.lenght/numberOfLetters)+1 times, your while - for each word. enumerateSubstringInRage:options: is executing 1 time for each while, because is getting the last word only.

Related

What would be a way to get all combinations of a NSString in Objective-C in a specific pattern?

I have a NSString *strName = #"JonnySmith";
What I want to do is get an NSArray of NSStrings with all possible combinations of a name, omitting certain characters. For example:
#"J";
#"Jo";
#"Jon";
but also combinations like:
#"JSmith";
#"JonSmith"
#"JonnSm";
#"JonSmt";
#"Smith";
#"th";
But they need to be in the order of the original name (the characters can't be out of order, just omitted). Basically traversing left to right in a loop, over and over again, until all possible combos are made.
What is the most efficient way to do this in Objective-C without make a mess?
Let's see if we can give you some pointers, everything here is abstract/pseudocode.
There are 2^n paths to follow, where n is the number of characters, as at each character you either add it or do not.
Taking your example after the first character you might produce #"" and #"J", then to each of these you either add the second character or not, giving: #"", #"J" (add nothing), #"o", "#Jo". Observe that if you have repeated characters anywhere in your input, in your sample you have two n's, this process may produce duplicates. You can deal with duplicates by using a set to collect your results.
How long is a character? Characters may consist of sequences of unicode code points (e.g. 🇧🇪 - Belgium flag, if it prints in SO! Letters can be similarly composed), and you must not split these composed sequences while producing your strings. NSString helps you here as you can enumerate the composed sequences invoking a block for each one in order.
The above give you the pseudocode:
results <- empty set
for each composed character in input do block:
add to results a copy of each of its members with the composed character appended
You cannot modify a collection at the same time you enumerate it. So "add to results" can be done by enumerating the set creating a new collection of strings to add, then adding them all at once after the enumeration:
new items <- empty collection
for every item in results
add to new items (item appending composed character)
results union new items
Optimising it slightly maybe: in (2) we had the empty string and in (4) we append to the empty string. Maybe you could not add the empty string to start and initialise new items to the composed character?
Hint: why did I write the non-specific collection in (4)?
Have fun. If you code something up and get stuck ask a new question, describe your algorithm, show what you've written, explain the issue etc. That will (a) avoid down/close votes and (b) help people to help you.
One possibility is to consider every combination to be a mask of bits, where 1 means the character is there and 0 means the character is missing, for example:
100010000 for JonnySmith will mean JS
000000001 for JonnySmith will mean h
It's simple to generate such masks because we can just iterate from 1 (or 000000001) to 111111111.
Then we only have to map that mask into characters.
Of course, some duplicates are generated because 1110... and 1101... will both be mapped to Jon....
Sample implementation:
NSString *string = #"JonnySmith";
// split the string into characters (every character represented by a string)
NSMutableArray<NSString *> *characters = [NSMutableArray array];
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
[characters addObject:substring];
}];
// let's iterate over all masks
// start with zero if you want empty string to be included
NSUInteger min = 1;
NSUInteger max = (1 << characters.count) - 1;
NSMutableString *buffer = [[NSMutableString alloc] initWithCapacity:characters.count];
NSMutableSet *set = [NSMutableSet set];
for (NSUInteger mask = min; mask <= max; mask++) {
[buffer setString:#""];
// iterate over all bits in the generated mask, map it to string
for (NSInteger charIndex = 0; charIndex < characters.count; charIndex++) {
if ((mask & (1 << (characters.count - charIndex - 1))) != 0) {
[buffer appendString:[characters objectAtIndex:charIndex]];
}
}
// add the resulting string to Set, will handle duplicates
[set addObject:[buffer copy]];
}
NSLog(#"Count: %#", #(set.count)); // 767
The size for NSUInteger will give us the maximum number of characters we can use using this method.
Noticed the question is old but no answer is accepted. I think you can generate all permutations and then omit results which don't match your criteria (or tweak this code per your needs)
#interface NSString (Permute)
- (NSSet *)permutations;
#end
#implementation NSString (Permute)
- (NSSet *)permutations {
if ([self length] <= 1) {
return [NSSet setWithObject:self];
}
NSMutableSet *s = [NSMutableSet new];
[s addObject:[self substringToIndex:1]];
for (int i = 1; i < self.length; i++) {
char c = [self characterAtIndex:i];
s = [self words:s insertingLetterAtAllPositions:[NSString stringWithFormat:#"%C",c]];
}
return [s copy];
}
- (NSMutableSet *)words:(NSSet *)words insertingLetterAtAllPositions:(NSString *)letter {
NSMutableSet *collector = [NSMutableSet new];
for (NSString *word in words) {
[collector unionSet:[word allInsertionsOfLetterAtAllPositions:letter]];
}
return collector;
}
- (NSMutableSet *)allInsertionsOfLetterAtAllPositions:(NSString *)letter {
NSMutableSet *collector = [NSMutableSet new];
for (int i = 0; i < [self length] + 1; i++) {
NSMutableString *mut = [self mutableCopy];
[mut insertString:letter atIndex:i];
[collector addObject:[mut copy]];
}
return collector;
}
#end
// usage
[#"abc" permutations];
You can do it quite easily with a little recursion. It works like this:
Check if the length is 1, then return an array of 2 elements, the empty string and the string.
Call recursively with input string minus the first character and assign to sub-result.
Duplicate the sub-result, adding the first character to each string.
Return the result.
Remember to not call for empty string. If you want to omit the empty result string just remove the first element. Also, if you use the same letter several times, you will get some result strings several times. Those can be removed afterwards.
- (void)combinations:(NSString *)string result:(NSMutableArray *)result {
if (string.length == 1) {
[result addObjectsFromArray:#[ #"", string ]];
} else {
[self combinations:[string substringFromIndex:1] result:result];
for (NSInteger i = result.count - 1; i >= 0; --i)
[result addObject:[[string substringToIndex:1] stringByAppendingString:result[i]]];
}
}
// Call like this, for speed only one mutable array is allocated
NSString *test = #"0123456789";
NSMutableArray *result = [NSMutableArray arrayWithCapacity:1 << test.length];
[self combinations:test result:result];

Get the unique characters in an NSString

How can I get the unique characters in an NSString?
What I'm trying to do is get all the illegal characters in an NSString so that I can prompt the user which ones were inputted and therefore need to be removed. I start off by defining an NSCharacterSet of legal characters, separate them with every occurrence of a legal character, and join what's left (only illegal ones) into a new NSString. I'm now planning to get the unique characters of the new NSString (as an array, hopefully), but I couldn't find a reference anywhere.
NSCharacterSet *legalCharacterSet = [NSCharacterSet
characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-()&+:;,'.# "];
NSString *illegalCharactersInTitle = [[self.titleTextField.text.noWhitespace
componentsSeparatedByCharactersInSet:legalCharacterSet]
componentsJoinedByString:#""];
That should help you. I couldn't find any ready to use function for that.
NSMutableSet *uniqueCharacters = [NSMutableSet set];
NSMutableString *uniqueString = [NSMutableString string];
[illegalCharactersInTitle enumerateSubstringsInRange:NSMakeRange(0, illegalCharactersInTitle.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (![uniqueCharacters containsObject:substring]) {
[uniqueCharacters addObject:substring];
[uniqueString appendString:substring];
}
}];
Try with the following adaptation of your code:
// legal set
NSCharacterSet *legalCharacterSet = [NSCharacterSet
characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789-()&+:;,'.# "];
// test strings
NSString *myString = #"LegalStrin()";
//NSString *myString = #"francesco#gmail.com"; illegal string
NSMutableCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:myString];
// inverts the set
NSCharacterSet *illegalCharacterSet = [legalCharacterSet invertedSet];
// intersection of the string set and the illegal set that modifies the mutable stringset itself
[stringSet formIntersectionWithCharacterSet:illegalCharacterSet];
// prints out the illegal characters with the convenience method
NSLog(#"IllegalStringSet: %#", [self stringForCharacterSet:stringSet]);
I adapted the method to print from another stackoverflow question:
- (NSString*)stringForCharacterSet:(NSCharacterSet*)characterSet
{
NSMutableString *toReturn = [#"" mutableCopy];
unichar unicharBuffer[20];
int index = 0;
for (unichar uc = 0; uc < (0xFFFF); uc ++)
{
if ([characterSet characterIsMember:uc])
{
unicharBuffer[index] = uc;
index ++;
if (index == 20)
{
NSString * characters = [NSString stringWithCharacters:unicharBuffer length:index];
[toReturn appendString:characters];
index = 0;
}
}
}
if (index != 0)
{
NSString * characters = [NSString stringWithCharacters:unicharBuffer length:index];
[toReturn appendString:characters];
}
return toReturn;
}
First of all, you have to be careful about what you consider characters. The API of NSString uses the word characters when talking about what Unicode refers to as UTF-16 code units, but dealing with code units in isolation will not give you what users think of as characters. For example, there are combining characters that compose with the previous character to produce a different glyph. Also, there are surrogate pairs, which only make sense when, um, paired.
As a result, you will actually need to collect substrings which contain what the user thinks of as characters.
I was about to write code very similar to Grzegorz Krukowski's answer. He beat me to it, so I won't but I will add that your code to filter out the legal characters is broken because of the reasons I cite above. For example, if the text contains "é" and it's decomposed as "e" plus a combining acute accent, your code will strip the "e", leaving a dangling combining acute accent. I believe your intent is to treat the "é" as illegal.

How to filter a string after a particular character in iOS? [duplicate]

This question already has answers here:
Split an NSString to access one particular piece
(7 answers)
Closed 9 years ago.
I want to filter string after character '='. For eg if 8+9=17 My output should be 17. I can filter character before '=' using NSScanner, how to do its reverse??? I need a efficient way to do this without using componentsSeparatedByString or creating an array
Everyone seems to like to use componentsSeparatedByString but it is quite inefficient when you just want one part of a string.
Try this:
NSString *str = #"8+9=17";
NSRange equalRange = [str rangeOfString:#"=" options:NSBackwardsSearch];
if (equalRange.location != NSNotFound) {
NSString *result = [str substringFromIndex:equalRange.location + equalRange.length];
NSLog(#"The result = %#", result);
} else {
NSLog(#"There is no = in the string");
}
Update:
Note - for this specific example, the difference in efficiencies is negligible if it is only being done once.
But in general, using componentsSeparatedByString: is going to scan the entire string looking for every occurrence of the delimiter. It then creates an array with all of the substrings. This is great when you need most of those substrings.
When you only need one part of a larger string, this is very wasteful. There is no need to scan the entire string. There is no need to create an array. There is no need to get all of the other substrings.
NSArray * array = [string componentsSeparatedByString:#"="];
if (array)
{
NSString * desiredString = (NSString *)[array lastObject]; //or whichever the index
}
else
{
NSLog(#""); //report error - = not found. Of array could somehow be not created.
}
NOTE:
Though this is very popular splitting solution, it is only worth trying whenever every substring separated by separator string is required. rmaddy's answer suggest better mechanism whenever the need is only to get small part of the string. Use that instead of this approach whenever only small part of the string is required.
Try to use this one
NSArray *arr = [string componentsSeparatedByString:#"="];
if (arr.count > 0)
{
NSString * firstString = [arr objectAtIndex:0];
NSString * secondString = [arr objectAtIndex:1];
NSLog(#"First String %#",firstString);
NSLog(#"Second String %#",secondString);
}
Output
First String 8+9
Second String 17
Use this:
NSString *a =#"4+6=10";
NSLog(#"%#",[a componentsSeparatedByString:#"="])
;
Log: Practice[7582:11303] (
"4+6",
10
)

Obj-c Turning long string into multidimensional array

I have a long NSString, something like "t00010000t00020000t00030000" and so on. I need to split that up into each "t0001000".
I'm using...
NSArray *tileData = [[GameP objectForKey:#"map"] componentsSeparatedByString:#"t"];
And it splits it up, but the "t" is missing, which I need (although I think I could append it back on). The other way I guess would be to split it up by counting 8 char's, although not sure how to do that.
But ideally I need it split into a [][] type array so I can get to each bit with something like...
NSString tile = [[tileData objectAtIndex:i] objectAtIndex:j]];
I'm new to obj-c so thanks for any help.
If they're not strictly the t characters that separate the sections, i. e. the parts are always 8 characters long, then it's very easy to do it:
NSString *string = #"t00010000t00020000t00030000";
NSMutableArray *arr = [NSMutableArray array];
int i;
for (i = 0; i < string.length; i += 8) {
[arr addObject:[string substringWithRange:NSMakeRange(i, 8)]];
}
and here arr will contain the 8-character substrings.
Edit: so let me also provide yet another solution for the multidimensional one. Of course #KevinH's solution with the characters is very elegant, but if you need an NSString and you don't mind implementing another method, it's fairly easy to add something like this:
#implementation NSString (EightCarAdditions)
- (NSString *)charAsStringInSection:(NSInteger)section index:(NSInteger)index
{
return [self substringWithRange:NSMakeRange(section * 8 + index, 1)];
}
- (unichar)charInSection:(NSInteger)section index:(NSInteger)index
{
return [self characterAtIndex:section * 8 + index];
}
#end
(Beware that characterAtIndex returns a unichar and not a char - be prepared for more than 1 byte-wide UTF-(8, 16, 32) stuff.) You can then call these methods on an NSString itself, so it's very convenient:
unichar ch = [string charInSection:1 index:3];
H2CO3's answer is spot-on for the first part. For the second (the multi-dimensional array), if I understand what you want, you don't need another array for the individual characters. For each NSString in the array, you can access each character by calling characterAtIndex. So, extending the example above:
for (NSString *item in arr) {
NSLog(#"The fifth character of this string is: %C", [item characterAtIndex:4]);
}
And if you're looking to chain these together, as in your example, you can do that too:
NSLog(#"The fifth character of the fourth string is: %C",
[[arr objectAtIndex:3] characterAtIndex:4]);

NSNumber storing float. Please NO scientific notation?

My code looks like this
NSNumber *inputToNumber = [NSNumber numberWithFloat:[textField.text floatValue]];
the value from the textfield is actually a telephone number. It's stored in NSNumber as an annoying (2.0)78966e+08
How can I just get NSNumber to store it as 0207896608?
I think that the basic idea to store a phone number into a NSNumber is flawed:
how do you discriminate between numbers with or without leading 0 ?
how do you store phone numbers from foreign countries ?
I would use NSString in place of NSNumber.
Just because it's called a number doesn't mean a "telephone number" is a number in the same sense that "5" or "pi" are.
Either you should treat a telephone number as a string, or you should create a TelephoneNumber model class to represent each one.
Consider that there are places in the world where numbers don't have leading 0's and where a number with a leading 0 is not the same as the same number without a leading 0.
05843924 != 5843924
So stop being lazy with that NSNumber hacks and build your own phone-number class.
Scientific notation is used in may computer languages as the default output of very large (or very small) numbers. If you want the number to be output as a decimal, you need to specify the output format (the implementation varies by language.)
Also, julesjacobs is correct. You should not use FLOAT for a phone number as it is subject to binary rounding errors. Using INT or STRING will save you lots of headaches.
If you need to be able to deal with it as numbers maybe you should break it up into its parts, and store each part as an integer.
01112223333
country code 0
area code 111
prefix 222
number 3333
Or you could store the whole thing as a string if you don't need to manipulate it.
Are you storing a phone number in a float? You should consider using an integer or string. Perhaps:
NSNumber *inputToNumber = [NSNumber numberWithInt:[textField.text intValue]];
Hey Guys what do you think of this, It seems to full-fill my purposes. Only UK at the moment so will worry about localization when I get a chance.
I use this to get to store the number
NSNumber *inputToNumber = [NSNumber numberWithLongLong:(long long)[[textField.text stringByReplacingOccurrencesOfString:#" " withString:#""] longLongValue]];
And this method formats my telephone number and takes care of the preceeding 0 mentioned.
-(NSString *)phoneNumberString:(NSNumber *)phoneNumber {
//Add a zero because NSNumber won't save a preceeding zero
NSString *telephoneString = [[NSString alloc] initWithFormat:#"0%#", [phoneNumber stringValue]];
if (telephoneString.length >= 4) {
NSString *firstPart = [[NSString alloc] initWithString: [telephoneString substringToIndex:4]];
NSString *secondPart = [[NSString alloc] initWithString: [telephoneString substringFromIndex:4]];
//Add the two parts together with a space inbetween
NSString *formattedTelephoneString = [NSString stringWithFormat:#"%# %#", firstPart, secondPart];
//send it back to the cellForRow TableCell Method
[firstPart release];
[secondPart release];
[telephoneString release];
return formattedTelephoneString;
}
else {
return telephoneString;
}
}
Thanks for all the comments. I'm gonna mark the answer as whoever suggested NSString as I fear I will revert to using NSString for this instead of my above workaround.

Resources