Comparing Strings in Objective-C - iphone

I'm writing a small app for the iphone and I'm trying to write a function that will insert an NSMutableString into an NSArray in alphabetical order. Also I'll be writing a sort to sort the entire array as well. For both cases I'm wondering what the best way of comparing NSMutableStrings is. Is there a specific function I can use?
Thanks for your help.

I think you're looking for
(NSComparisonResult)[aString compare: bString];
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/nsstring_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/compare:
You can use this or one of the related methods if you're doing insertion sort. However, if you want to do a one time sort of the NSMutableArray, you can use one of the NSMutableArray sorting methods such as sortUsingComparator:.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableArray/sortUsingComparator:

If you look under "Identifying and comparing strings" in the NSString reference, you'll find several options. They do slightly different things, since you might want to compare strings in different ways (e.g. are numbers compared in lexical or numeric order?). The most basic is compare: — you can probably start there and choose a more complicated version as needed.

I think this should work for you. This is my answer which I have taken from the link:
Comparing text in UITextView?
SOLUTION-1: I have modified it here a bit to make it more easier for your case:
Let us assume String1 is one NSString.
//Though this is a case sensitive comparison of string
BOOL boolVal = [String1 isEqualToString:#"My Default Text"];
//Here is how you can do case insensitive comparison of string:
NSComparisonResult boolVal = [String1 compare:#"My Default Text" options:NSCaseInsensitiveSearch];
if(boolVal == NSOrderedSame)
{
NSLog(#"Strings are same");
}
else
{
NSLog(#"Strings are Different");
}
Here if boolVal is NSOrderedSame then you can say that strings are same else they are different.
SOLUTION-2: Also you don't find this easy, you can refer to Macmade's answer under the same link.
Hope this helps you.

For sorting array
NSArray *sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];

try BOOL ans = [str1 isEqualToString:str2];

Try NSArray's sortedArrayUsingSelector: method:
NSArray * stringArray = [NSArray arrayWithObjects:#"dddsss", #"aada", #"bbb", nil];
[stringArray sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#", [stringArray sortedArrayUsingSelector:#selector(compare:)]);
Out put:
(
aada,
bbb,
dddsss
)
What's more, you can use NSSortDescriptor to decide ASC or DESC order.

Related

How to turn NSString to a number

I need to sort a list of files according to the number after "_" :
"filename_1" "filename_2" ......
I know that I can extract the number, then sort the filename with the number, but I have to consider what if some illegal name exist and the program turn to be long.
What I want to do is just simply compare the whole String, the same char must has the same number. But when I do [#"word" intValue], the result is zero if the String is not a number. I want to know is there a good way to turn a String into a number.
Take a look at NSString's method:
- (NSComparisonResult)compare:(NSString *)aString options:(NSStringCompareOptions)mask
and the NSNumericSearch option.
If you wish to sort them on your own then you will need to use the
- (NSComparisonResult)compare:(NSString *)aString;
routine.
Here is the reference to the NSComparisonResult capable outputs.
These constants are used to indicate how items in a request are ordered.
enum {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
};
typedef NSInteger NSComparisonResult;
Constants:
NSOrderedAscending
The left operand is smaller than the right operand.
NSOrderedSame
The two operands are equal.
NSOrderedDescending
The left operand is greater than the right operand.
If you think it easier, your can fill an NSArray with your separated strings, then use something like the following:
sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
Link to a good QA from SO:
How to sort a NSArray alphabetically?
There is 1 final way I can think of in your case. That would be to use the hasPrefix method of NSString to make sure that each of your strings begins with "filename_" as a first validation. afterwards substring the overall string just getting the remaining string after the "filename_". If all of your filenames are to have a number of 1 or greater, then a 0 means the string is invalid at this point, else the intValue should return a valid positive integer, and you can sort via the integer values.
This should work:
NSString *good = #"filename_1";
NSString *bad = #"filename";
NSCharacterSet *letterSet= [NSCharacterSet letterCharacterSet];
good = [good stringyByTrimmingCharactersInSet:letterSet]; // 1
bad = [bad stringByTrimmingCharactersInSet:letterSet]; // <empty>
You can use following coe to sort the array containing the list of File Objects:
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"fileName" ascending:YES];
[arrayOfFileNames sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
Hope this is helpful to you...
I think you can not compare it with NSString because NSString follows 1,10,11,12,13,14 .....2,20,21,22 ......this type of ascending order while integer follows like 0,1,2,3,4,5,6,7....... and so on..... So you have to split the last character from string then will have to compare.
well how to short string in ascending order ....
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"f_1",#"f_2",#"f_12",#"f_6",#"f_9",#"f_4", nil];
NSSortDescriptor *name = [[[NSSortDescriptor alloc] initWithKey:#"" ascending:YES] init];
[array sortUsingDescriptors:[NSMutableArray arrayWithObjects:name, nil]];
but this will give you string shorting not according to number.
Thank You!

iOS iPhone how to list all keywords in a UTextView by frequency of use?

I got a UITextView with an arbitrary length text (up to 10000 characters). I need to parse this text, extract all keywords and list them by the frequency of use with the most frequently used word being on top, next one down, etc. I will most likely present a modal UITableView after the operation is completed.
I'm thinking of an efficient and useful way to do this. I can try to separate a string using a delimiter in the form of [whitespace, punctuation marks, etc].
This gets me an array of character sequences.
I can add each add sequence as an NSMutableDictionary key, and increment its count once I see another instance of that word. However, this may result in a list of 300-400 words, most having frequency of 1.
Is there a good way to implement the logic that I'm describing? Should I try to sort the array in alphabetical order and try some kind of "fuzzy" logic match? Are there any NSDataDetector or NSString methods that can do this kind of work for me?
An additional question is: how would I extract stuff like a, at, to, for, etc, and do not list them in my keyword list?
It would be great if I can take a look at a sample project that has already accomplished this task.
Thank you!
You can use CFStringTokenizer to get the word boundaries. For counting, you could use an NSMutableDictionary, as you suggested, or an NSCountedSet, which might be slightly more efficient.
If you're not interested in words that have a frequency of 1 (or some other threshold), you would have to filter them out after counting all the words.
For ignoring certain words (a, the, for...), you need a word list specific to the language of your text. The Wikipedia article on stop words contains a couple of links, e.g. this CSV file.
There are many approaches to do this.
You should definitely add all your Keywords to an array (or other collection object) and reference it/ iterate through it so you are searching for these keywords and only these keywords (and are avoiding checking for occurrences of a, at, to, for, etc.)
NSArray *keywords = [ add your keywords ];
NSString *textToSearchThrough = #" your text "; // or load your text File here
- loop control statement here (like maybe fast enumerate), and inside this loop:
NSRange range = [textToCheckThrough rangeOfString:keywords[currentKeyword]
options:NSCaseInsensitiveSearch];
if(range.location != NSNotFound) {
// meaning, you did find it
// add it to a resultsArray, add 1 to this keyword's occurrenceCounter (which you must also declare and keep track of)
// etc.
}
Then you loop through your results array, check number of occurrences per keyword, purge those who's occurrence count is < minOccurrenceCount, and sort remaining from highest to lowest.
I ended up going with the CFStringTokenizer . I'm not sure if the bridged casts below are correct, but it seems to work
-(void)listAllKeywordsInString:(NSString*)text
{
if(text!=nil)
{
NSMutableDictionary* keywordsDictionary = [[NSMutableDictionary alloc] initWithCapacity:1024];
NSString* key = nil;
NSLog(#"%#",text);
NSLog(#"Started parsing: %#",[[NSDate date] description]);
CFStringRef string =(__bridge CFStringRef)text; // Get string from somewhere
CFStringTokenizerRef tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, (__bridge_retained CFStringRef) text, CFRangeMake (0,CFStringGetLength((__bridge_retained CFStringRef)text)), kCFStringTokenizerUnitWord, CFLocaleCopyCurrent());
unsigned tokensFound = 0; // or the desired number of tokens
CFStringTokenizerTokenType tokenType = kCFStringTokenizerTokenNone;
while(kCFStringTokenizerTokenNone != (tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)) ) {
CFRange tokenRange = CFStringTokenizerGetCurrentTokenRange(tokenizer);
CFStringRef tokenValue = CFStringCreateWithSubstring(kCFAllocatorDefault, string, tokenRange);
// This is the found word
key =(__bridge NSString*)tokenValue;
//increment its count
NSNumber* count = [keywordsDictionary objectForKey:key];
if(count!=nil)
{
[keywordsDictionary setValue:[NSNumber numberWithInt:1] forKey:key];
}else {
[keywordsDictionary setValue:[NSNumber numberWithInt:count.intValue+1] forKey:key];
}
CFRelease(tokenValue);
++tokensFound;
}
NSLog(#"Ended parsing. tokens Found: %d, %#",tokensFound,[[NSDate date] description]);
NSLog(#"%#",[keywordsDictionary description]);
// Clean up
CFRelease(tokenizer);
}
}

why does '[self.pickerSubArray indexOfObject:self.txtSubCategory.text]' return '2147483647'?

why does '[self.pickerSubArray indexOfObject:self.txtSubCategory.text]' return '2147483647'; while the same string value argument '[self.pickerSubArray indexOfObject:#"Mark"]' brings up 4, as desired?
The Apple docs for NSArray (which I assume your object is, based on the name) say that indexOfObject: returns NSNotFound if the object does not match any in the array. NSNotFound is itself defined as NSIntegerMax which, as others have pointed out, is the value that you are getting back.
indexOfObject: uses isEqual: to compare the items, so in theory if the text is the same then it should be working. Perhaps the text is actually different in some way that you haven't noticed, such as case ("Mark" vs. "mark") or extra padding ("Mark" vs. "Mark ").
indexOfObject: returns NSNotFound if it can't find your exact object. NSNotFound is defined as NSIntegerMax, which is 2147483647.
Why is it doing that? I'm pretty sure indexOfObject: tests for an identical object, not an object with identical content.
e.g.
NSString *mark1 = [NSString stringWithString:#"Mark"];
NSString *mark2 = [NSString stringWithString:#"Mark"];
mark1 is not necissarily equal to mark2, because they're two different objects.
NSString *mark1 = [NSString stringWithObject:#"Mark"];
NSString *mark2 = mark1;
mark1 is equal to mark2;
BUT! Since the compiler is trying to minimize the memory footprint, it turns all literal strings in your code into one constant string. Which is why [[NSArray arrayWithObject:#"Mark"] indexOfObject:#"Mark"] works, but [[NSArray arrayWithObject:#"Mark"] indexOfObject:textField.text] doesn't work even if the text in textField.text is "Mark".
How do you fix it... well, indexOfObject: from the docs it looks like indexOfObject: is based on isEqual: so you should test if [self.txtSubCategory.text isEqual:#"Mark"]. to rule out the wrong value or a disconnected outlet, etc. After that, you may have to refactor to not use indexOfObject:
Just a guess about the number origin - it's a bad integer conversion. It was very probably meant to return -1.
That kindof leads me to believe that you might have found some badness in underlying libraries/languages.

NSDictionary split into two arrays (objects and keys) and then sorted both by the objects array (or a similar solution)

I have an NSDictionary. It has keys and objects.
For the purposes of simplicity the keys are Question numbers and the objects are calculated Answer scores.
Now how I did it before was that I set the answer score as the keys and the question numbers as the objects. This way I could get an array of allKeys from the dictionary, sort it and then do something similar to:
for(NSString *string in tempArray){
NSLog(#"%#",[dictionary objectForKey:string]);
}
The (stupid - on my part) problem that I have now encountered however is that (obviously... duuhhh) the keys need to unique, and therefore when the calculated answer scores are the same, only one answer gets output!
I need a solution to this. In PHP you can multisort arrays. I was wondering if there was some similar solution in objective-c or indeed if someone had a better answer?
Any help here would be much appreciated.
Thank you.
Do you know about the allKeys, allValues and allKeysForObject method of the NSDictionary do you ?
One solution is to store the answer scores using an array of dictionaries containing only two key-value pairs. One key is the question number (or however your questions are tagged, i.e. “Q1.1”), while the other key is the actual answer score. For example:
static NSString * const QuestionKey = #"questionNumber";
static NSString * const AnswerScoreKey = #"answerScore";
NSMutableArray *allAnswers = [NSMutableArray array];
for (int i = 0; i < 20; i++)
{
// fill allAnswers array with random data
NSDictionary *answer = [NSDictionary dictionaryWithObjectsForKeys:
[NSString stringWithFormat:#"Q%d", i], QuestionKey,
[NSNumber numberWithInt:rand()], AnswerScoreKey,
nil];
[allAnswers addObject:answer];
}
// sort the allAnswers array based on score, highest first
NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:AnswerScoreKey ascending:NO];
[allAnswers sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
for (NSDictionary *answer in allAnswers)
{
NSLog(#"Question: %#, AnswerScore: %#", [answer objectForKey:QuestionKey], [answer objectForKey:AnswerScoreKey];
}
Disclaimer:
Untested and uncompiled code. Theory only.
I am not sure what you are actually trying to achieve. Do you want to sort an array of dictionary objects based on the value of the dictionary? If that is what you want, you can define your own comparator function and can define any custom sorting behavior. Please check sortedArrayUsingSelector: method of NSArray.
Edit : I am away from Mac currently but this previous question has a number of example code that can solve your problem. Rather than using object, you can use NSDictionary, though personally I would like to use another Question object instead of dictionary in this case. This question class will contain two value, id and score and then you need to sort the array of questions based on score just like the persons are sorted based on birthday.

Objective C: Create arrays from first array based on value

I have an array of strings that are comma separated such as:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
Bill Gates,44,WA
Bill Nye,21,OR
I have those values in an NSScanner object so that I can loop through the values and get each comma seperated value using objectAtIndex.
So, what I would like to do, is group the array items into new arrays, based on a value, in this case, State. So, from those, I need to loop through, checking which state they are in, and push those into a new array, one array per state.
CA Array:
Steve Jobs,12,CA
Fake Name,21,CA
Test Name,22,CA
WA Array:
Bill Gates,44,WA
OR Array:
Bill Nye,21,OR
So in the end, I would have 3 new arrays, one for each state. Also, if there were additional states used in the first array, those should have new arrays created also.
Any help would be appreciated!
You can use a NSMutableDictionary of NSMutableArrays - if the state encountered isn't yet in the dictionary, add a new array.
NSMutableArray* arr = [states objectForKey:state];
if (arr == nil) {
arr = [NSMutableArray array];
[states setObject:arr forKey:state];
}
Then you can insert values into the array, preferably as objects though as Dave DeLong mentions.
You shouldn't be maintaining this data as CSV. That's asking for a world of hurt if you ever need to manipulate this data programmatically (such as what you're trying to do).
You can naïvely break this data up into an array using NSArray * portions = [line componentsSeparatedByString:#","];. Then create a custom object to store each portion (for an example, see this post), and then you can manipulate those objects almost effortlessly.
Naively: (assuming array of strings called strings)
NSMutableDictionary *states = [NSMutableDictionary dictionary];
for (NSString *string in strings) {
NSString *state = [[string componentsSeparatedByString:#", "] lastObject];
NSMutableArray *values = [states objectForKey:state];
if (values == nil) {
values = [NSMutableArray array];
[states setObject:value forKey:state];
}
[values addObject:string];
}
Number of things about this -- first of all, I'm not at my computer, so there is a high chance of typos and or things that I missed. Second, you probably want to adapt the components separated by string line to handle whitespace better.