Objective-C: Comparing array of strings to user-entered string, then returning appropriate value? - objective-c

This is a question about iOS programming with Objective C.
I have an NSMutableArray of strings "csvContent", that were parsed from a CSV file that contained a pseudo-database of questions, answers, and keywords. The contents of the CSV file were as follows:
ID#, "Here is the question I am asking?", "[question, key, words]", "This is the answer to your question."
There are about 2,000 of these questions and related keywords and answers, and I have successfully parsed them into the array, line by line so that each element contains everything in the example you see above.
My question is that if I want to have a user ask a question in a UITextField and then compare the UserQuestion and find the most similar question in my array of strings and then return its answer, what would be the best way to go about doing so? I've looked through documentation about Levenshtein distance and think that that would be a good option, but don't know how to exactly implement it and have it iterate through my entire CSVContent array. I'm not looking for exact code, but an ideal answer would contain some pseudocode or methodology on how to go about this.
To summarize:
Array of strings, CSVContent, of appearance: [id,"question",("question keywords"),"answer"].
I have a UITextField where I can parse a user's entered question into a string UserQuestion.
I want to use a fast comparison algorithm (Levenshtein?) to compare UserQuestion to the elements inside CSVContent and find the appropriate question and related answer, then return the answer.

When user hits the Search button, pass textField.text to this method:
- (int)matchingIDForString:(NSString *)userSuppliedText {
NSInteger bestLevDistanceSoFar = 9999999;
int indexOfMatch=-1;
// having to search the entire array each time is, of course, scary.
for ( int j=0; j<[myMutableArray count]; j++ ) {
NSString *candidateAnswer = [myMutableArray objectAtIndex:j];
// if candidateAnswer is a string, just use it. Else extract the member you want...
NSInteger *levDistance = [self myLevensteinDistanceMethod:candidateAnswer
forstring:userSuppliedText];
if ( levDistance < bestLevDistanceSoFar ) {
indexOfMatch = j;
bestLevDistanceSoFar = levDistance;
}
}
return indexOfMatch; // called should test for <0 meaning no match
}
You'll need to implement this method also:
- (NSInteger *)myLevensteinDistanceMethod:(NSString *)string1 forString:(NSString *)string2 {
// calculate the lev distance, and return it as an NSInteger *.
}

Related

Accessing Attributes

I am new to programming and objective C, sorry I have to ask this basic question. I can not figure out the lines of code below. Why do I need to use & sign for the range when assess attributes? but not use it when I call the attribute again? is it because the first one is a setter and second one is getter?
Thanks for your advises in advances!
-(NSAttributedString*)characterWithAttribute: (NSString*)attributeName{
NSMutableAttributedString* characters = [[NSMutableAttributedString alloc]init];
int index = 0;
while(index < [self.textToAnalyze length]){
NSRange range;
id value = [self.textToAnalyze attribute:attributeName atIndex:index effectiveRange:&range];
if(value){
[characters appendAttributedString:[self.textToAnalyze attributedSubstringFromRange:range]];
index = (int)range.location + (int)range.length;
}
else{
index++;
}
}
return characters;
}
is it because the first one is a setter and second one is getter?
Basically, yes. The ampersand specifies the address of the variable rather than the value. When the called routine has an address, it can assign a value to it for you to use when it returns. If the called routine only had a value, it wouldn't be able to replace it in any way that was accessible to the caller.
Here's a link that might give you more in-depth details:
https://www.codingunit.com/c-tutorial-call-by-value-or-call-by-reference

Finding array objects from user input

I'm trying to make it so the user input for my code corresponds with the objects in my array, but I have no idea how to do this.
Basically, the assignment says to right a program that will grade final exams, each question has one of four possible answers ( a,b,c,d) and the first answer in the array should correspond to the first question in the exam. The program should them prompt the user for their answers to the exam, and should be compared with the first correct answer in the array and if it matches, it'll give them points.
Problem is, I can't for the life of me figure out how to compare user input to my array to see if it's the right thing they put in.
Here's what I have so far!
I know I'm awful at it, but I'm trying my best. Any help will be extremely appreciated.
#import <Foundation/Foundation.h>
int main (int argc, char * argv[])
{
#autoreleasepool {
NSArray *correctAnswers= [NSArray arrayWithObjects: #"a", #"b ", #"c", #"d",nil];
int sum = 0;
int anwser;
{
NSLog(#"Please input test anwsers starting with 1:");
scanf("%i",&anwser);
}
if ([correctAnswers containsObject:#(anwser)]) {
sum = +10;
}else {
NSLog(#"Well that's not quite right...");
}
NSLog(#"The final score is:%d",sum);
}
return 0;
}
#"a" etc are NSString instances.
You would need to use NSString instance methods to compare them to the user input. You can also do fast enumeration with collections like NSArray
for (NSString* ans in correctAnswers)
{
if ([ans isEqualtoString:[NSString stringWithInt:anwser]]) sum+=10;
}
You could also use NSArray's filteredArrayUsingPredicate: to search the array.
But you can't use containsObject to compare strings... it just checks for a specific NSObject instance.
You also appear to be comparing an int to a char... but that's outside the scope of your question.

Converting a string variable from Binary to Decimal in Objective C

Im trying to create a Binary to Decimal calculator and I am having trouble doing any sort of conversion that will actually work. First off Id like to introduce myself as a complete novice to objective c and to programming in general. As a result many concepts will appear difficult to me, so I am mostly looking for the easiest way to understand and not the most efficient way of doing this.
I have at the moment a calculator that will accept input and display this in a label. This part is working fine and I have no issues with it. The variable that the input is stored on is _display = [[NSMutableString stringWithCapacity:20] retain];
this is working perfectly and I am able to modify the data accordingly. What I would like to do is to be able to display an NSString of the conversion in another label. At the moment I have tried a few solutions and have not had any decent results, this is the latest attempt
- (NSMutableString *)displayValue2:(long long)element
{
_str= [[NSMutableString alloc] initWithString:#""];
if(element > 0){
for(NSInteger numberCopy = element; numberCopy > 0; numberCopy >>= 1)
{
[_str insertString:((numberCopy & 1) ? #"1" : #"0") atIndex:0];
}
}
else if(element == 0)
{
[_str insertString:#"0" atIndex:0];
}
else
{
element = element * (-1);
_str = [self displayValue2:element];
[_str insertString:#"0" atIndex:0];
NSLog(#"Prima for: %#",_str);
for(int i=0; i<[_str length];i++)
_str = _display;
NSLog(#"Dopo for: %#",_str);
}
return _str;
}
Within my View Controller I have a convert button setup, when this is pressed I want to set the second display field to the decimal equivalent. This is working as if I set displayValue2 to return a string of my choosing it works. All I need is help getting this conversion to work. At the moment this bit of code has led to "incomplete implementation" being displayed at the to of my class. Please help, and cheers to those who take time out to help.
So basically all you are really looking for is a way to convert binary numbers into decimal numbers, correct? Another way to think of this problem is changing a number's base from base 2 to base 10. I have used functions like this before in my projects:
+ (NSNumber *)convertBinaryStringToDecimalNumber:(NSString *)binaryString {
NSUInteger totalValue = 0;
for (int i = 0; i < binaryString.length; i++) {
totalValue += (int)([binaryString characterAtIndex:(binaryString.length - 1 - i)] - 48) * pow(2, i);
}
return #(totalValue);
}
Obviously this is accessing the binary as a string representation. This works well since you can easily access each value over a number which is more difficult. You could also easily change the return type from an NSNumber to some string literal. This also works for your element == 0 scenario.
// original number wrapped as a string
NSString *stringValue = [NSString stringWithFormat:#"%d", 11001];
// convert the value and get an NSNumber back
NSNumber *result = [self.class convertBinaryStringToDecinalNumber:stringValue];
// prints 25
NSLog(#"%#", result);
If I misunderstood something please clarify, if you do not understand the code let me know. Also, this may not be the most efficient but it is simple and clean.
I also strongly agree with Hot Licks comment. If you are truly interested in learning well and want to be an developed programmer there are a few basics you should be learning first (I learned with Java and am glad that I did).

Optimise searching in an array, search by comparison of 2 strings in Objective C

I have a list of contacts retrieved from Address book stored inside a MutableArray contactList. Each contact is an object which has properties like "contactName, contactImage.... etc".
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
//getAllContacts is a method which returns a Mutable array of Objects
self.contactList = [NSMutableArray arrayWithArray:[instance getAllContacts]];
//groupLetterToLoad could be "DEF"
for(int j=0; j<self.groupLetterToLoad.length;j++) {
//1st iteration D, 2nd iteration E and 3rd iteration F
NSString *testChar = [NSString stringWithFormat:#"%c",[self.groupLetterToLoad characterAtIndex:j]];
//check D,E,F with contact name property's first letter of the contact list array
for(int i=0;i<self.contactList.count;i++) {
NSString *firstChar =[[[self.contactList objectAtIndex:i] contactName] substringToIndex:1];
if([testChar isEqualToString: firstChar]) {
pos=i; //retrieve the index of the matched position
break;
}
}
if(pos!=-1) break;
}
});
Now this has two for loops (Time O(n^2)).. The disadvantage here is, if the groupLetterToLoad is "WXYZ", then comparison will start from W with A to W with Z.. How can I optimise it?
Ordering your array by contactName and performing a half interval search will reduce your complexity greatly if can avoid sorting every time you search (hint: keep [instance getAllContacts] sorted).
http://rosettacode.org/wiki/Binary_search#Objective-C - that's a starting point. you could replace the compare: with your first character comparison.
This isn't an algorithmic improvement, but the way you're handling characters is about the slowest way possible. If your group letters are really ASCII letters as you indicate, try this (I include the "if" in my answer because doing correct comparison of non-ASCII is really best left up to NSString):
1) Instead of using -substringToIndex to get the first character, use -characterAtIndex:0 and store a unichar
2) Instead of using +stringWithFormat:#"%c" to make a single character string, just use -characterAtIndex: and store it in a unichar
3) Instead of using -isEqualToString:, use == on the unichars
Unrelated, I'm pretty suspicious of the thread-safety of this. Are all those properties on self and instance you're accessing really not accessed on any other queue or thread?

compaire NSArray items with every other item in the NSArray

I have an NSArray of NSStrings and would like to know how to compare each item in the array with every other item in the array to see if there is any strings different from the rest.
I have seen a c++ example
for (int i = 0; i < list.size(); i++) {
for (int j = i+1; j < list.size(); j++) {
// compare list.get(i) and list.get(j)
}
}
but was woundering if there is a better easier way in objective C? also the other thing I need to do is make sure the item doesn't compare itself while it loops through.
Any help or examples would be greatly appreciated.
UPDATE ** BOLD is the updated part of the question **
If I read your question correctly, you want the strings that only appear once in the list, correct?
NSCountedSet *counted = [NSCountedSet setWithArray:list];
for (NSString *string in counted) {
NSUInteger count = [counted countForObject:string];
if (count == 1) {
// process "string", it appears in the list just once
}
}
If you just want to know if there is more than one different value in the list then do this:
NSSet *set = [NSSet setWithArray:list];
if (set.count == 1) {
// There is only one distinct value in the list
} else {
// There is more than one distinct value in the list
}
I'd use an NSMutableDictionary. This is very similar to "merging two lists into unique values", the apple docs actually explain the complicated way somewhere. I forgot where I found it, but the easy way is here: Merge two arrays while preserving the original array order
So what you'd do is loop through everything, see if there's a key (set to the string), if not, add one via the setObject: forKey: method, then enumerate through the dictionary or just grab the allKeys value after.
Use two sets. If the string goes into the first set without conflict, add it to the second set. If the string encounters a conflict in the first set, remove it from the second set. When you've processed all the strings the second set contains the unique ones.