I have setup an array full of words as objects. I am trying then trying to compare what the user enters in a uitextfield to an object in the array to see if they match.
I have captured a pointer to the uitextview in one of the delegate methods, however I cannot compare the text field contents to the object in the array. I assume that this is because one is a string and one is an array object ? Do I need to cast one of the variables somehow ?
Thanks
Martin
Assuming you want to test whether the user's input matches any member of your array, you should do something like this:
NSString *input = textField.text;
BOOL wordFound = NO;
for(NSString *candidate in self.possibleWordsArray) {
if( [input isEqual:candidate] ) {
wordFound = YES;
break;
}
}
if(wordFound) {
...
}
else {
...
}
Notes:
We are comparing the input string against each member of the array in turn. This gives an explicit meaning to the idea of matching a string against an array.
When we find a matching string, we set the flag wordFound to YES and then stop searching (using the break statement). If no match is found, the wordFound flag remains at its original value of NO.
Always use isEqual to test equality between strings, never ==; the later will usually work, but then break occasionally. This is because == only tests whether the two pointers point to the same memory location, whereas you might have two copies of an identical string in different memory locations.
Related
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?
I've been trying to logically think about this for a while now and usually I can solve it by writing it out or thinking about it while doing other stuff not associated with programming. However everything I try isn't working. So basically I have 2 NSArrays, which are both populated from 2 different plists. Each array is filled with all dictionaries: all have the same keys, and all have the same identical structure, but some may have information associated with a key that some don't. I basically need to check each item in array1 and see if it exists in array2 and if it does NOT exist in array2 then add it to array1 (or array3 seeing how you can't mutate an array while iterating through it). I need it to see if it exists by a specific key "name_key".
So... In short my end result needs to be an NSArray with all objects from both arrays without having objects with duplicate names (from the dictionaries name_key).
Below is my actual code that doesn't work.
IN CODE BELOW: originalWhiskiesListArray = array1
newWhiskiesListArray = array2
combinedWhiskiesListArray = array3 (because you can't mutate an array while iterating through it.
BOOL whiskyExists = YES;
for (NSDictionary *newWhisky in newWhiskiesListArray) {
for (NSDictionary *originalWhisky in originalWhiskiesListArray) {
NSString * newWhiskyNameString = [[newWhisky objectForKey:NAME_KEY] lowercaseString];
NSString * origWhiskyNameString = [[originalWhisky objectForKey:NAME_KEY] lowercaseString];
//Compare lowercase strings and if they don't match then add them to the original plist array.
if ([newWhiskyNameString isEqualToString:origWhiskyNameString]) {
whiskyExists = YES;
break;
} else {
whiskyExists = NO;
break;
//NSLog(#"Whisky names do not match. New Whisky: %# Old Whisky: %#",[newWhisky objectForKey:NAME_KEY],[originalWhisky objectForKey:NAME_KEY]);
//doesn't match so add it
}
}
if (whiskyExists == NO) {
[combinedWhiskiesListArray addObject:newWhisky];
NSLog(#"newWhisky added");
whiskyExists = YES;
}
}
Can either of the whiskey name strings be nil? If so then this breaks the isEqualToString comparison because you can always message nil which returns NO.
Also I believe that the breaks are wrong. You only need to exit the inner loop in case you encounter a match. If not you have to keep going until the end of the inner loop.
If I'm understanding you correctly, you can just add all the values from each dictionary to an NSMutableSet, which won't add an item if it already exists in the set. You can then convert the set back to an array with the NSSet method allObjects.
I'd like to have some hints about one thing:
In an array, bound to a tableview column, I have some numeric (float) values. These values are entered (put into the array) using interface buttons, labelled "0", "1", "2" and so on. There is also a "C" button to cancel an entry.
Later, I have some calculations to do with this array. If no result was entered, the array's object cannot be 0. "No value" is certainly better represented by "nil"… but you are not allowed to insert a nil object into an array (nil means "end of array").
A NSArray contains objects. That is, here, these object must be NSNumbers. So I can initialize my array by filling it with [NSNumber numberWithDouble:…] double what? I cannot put nil here!
A true NSArray does not (can not) contain "holes". But mine has to, if for example, the third item never received a value. How can I:
implement an "undefined" value for my NSNumber?
make the "C" button erase a previously entered result?
There must be a solution. In ApplescriptObjC I used a trick: I did the attribution in the method called by every button, using its title, so:
if sender's title as string is "C" then
set item theRow of gResult to missing value -- the row was the selected line, gResult was a AS list.
else
set item theRow of gResult to sender's title as real
end if
What should I do here?
- choose a special value (say -1) to indicate an undefined value, then use a NSPredicate to filter my array?
- how could I turn the cell to "nothing" as the special value IS something and appear into the cell (even )
I'm sure there is an elegant solution to this problem. But for now I just tear out what little hair I have left…
Thank for help…
You can add an NSNull object to represent a null value.
An NSArray must always contains objects, nil is a flag indicating the end of the array. Thus in order to represent a nil value as an object you should use NSNull as follows:
[NSNull null]
You need a 'distinguished' object in your NSArray. When you see that distinguished object, your code does something different. Such a 'distinguished' object is often called a 'sentinel' and it can be anything. For example:
static NSNumber *theSentinel;
/* initialize theSentinel somewhere with */
theSentinel = [NSNumber numberWithFloat: <anyfloat-doesnt'-matter-which>];
then in your code:
NSNumber *selection = [NSArray objectAtIndex: <user selection>];
if (selection == theSentinel) { /* something */ }
else { /* main */ }
And in your predicates you can use (in words, not code):
predicate = (obj NOT EQUAL theSentinel) AND (rest of your predicate based on float)
I'm still trying to get my head around how selectors and dynamic typing work in objective c. I'm essentially trying to implement this method (shown below in python/pseudocode, same thing really :P)
def isInArray(value, array, test):
for item in array:
if test(item) == value:
return True
return False
test = lambda obj: obj.property
My intended objective-c code was:
+ (BOOL)value:(id)value:
isInArray:(NSMutableArray *)array
usingSelector:(SEL)selector {
for (id item in array) {
if (value == [item selector]) {
return YES;
}
}
return NO;
}
However, this throws a compile error, complaining that I"m comparing two pointer types (id and SEL) in the if statement.
Shouldn't that if statement be comparing the object value with the object returned from running SEL selector on the object arritem? In other words, why does it think that it's comparing an object with a SEL (I don't see what would be returning a SEL there)
Checkout performSelector in NSObject. The conditional would look like,
if (value == [item performSelector:selector])
By using Key Value Coding (KVC), your code could become even simpler. Say you have an array named people, where each person is an object of the class Person. A Person has firstName and lastName properties, and you want to get all people whose first name matches "John" from the people array. You could get an array of all first names, and then lookup the name "John" in that array.
NSArray *firstNames = [people valueForKey:#"firstName"];
if ([firstNames containsObject:#"John"]) {
..
}
Even though we're using valueForKey, it's actually making a call to the firstName method, and not directly accessing the value.
at least one mistake I see in method definition. There are no need to add colon after variable name (in your case value). And you should use performSelector: to make object check with your selector.
+ (BOOL)value:(id)value
isInArray:(NSMutableArray *)array
usingSelector:(SEL)selector {
for (id item in array) {
if ([item performSelector:selector] == value) {
return YES;
}
}
return NO;
}
In some cases you will need use isEqualTo: or isEqualToString: (if you work with strings) instead of == because == will return true if it's the same object.
Also, if you will need just check presence of some object in NSArray, than you could use containsObject: (return true if it is) and indexOfObjectPassingTest: (return index of object or 'NSNotFound')
I have a class with an accessible method that passes back an NSString when called.
[MyClass getMyString]
The string variable in that class is actually assigned in the didSelectRowAtIndexPath: part of a table like this:
myString = cell.textLabel.text;
When I retrieve the string by calling that method, I assign it to another string in the class that called it and compare it to a string I have defined
NSString *mySecondString;
mySecondString = #"my value";
if(mySecondString == myString){
i = 9;
}
I have stepped through the code and every time it evaluates the if statement, it skips right past the i=9 and goes to the next else if statement. Why would this be? Why don't they evaluate to be the same value? If you hover your cursor over each of the values during debugging they will show they have the same value, but the code for some reason with not do as I expect it to do and assign 9 to i.
Any thoughts?
You're assuming that the C == operator does string equality. It doesn't. It does pointer equality (when called on pointers). If you want to do a real string equality test you need to use the -isEqual: method (or the specialization -isEqualToString: when you know both objects are strings):
if ([mySecondString isEqualToString:myString]) {
i = 9;
}
You are comparing pointers to strings, rather than the strings themselves. You need to change your code to
if (if([mySecondString isEqualToString:myString]) {
....
}
you can not use '==' to compare two NSString
you should to use [NSString isEqualToString:(NSString*)] to compare two string
You can not compare the two string using "==" this is for int and other values.
you can use below code for the comparing two string
if ([Firststring isEqualToString:Secondstring]) {
NSLog(#"Hello this both string is same ");
}
It's a basic concept of pointer, you are missing. (YES, myString and mySecondString are pointers to the string).
Now, if(mySecondString == myString) will go TRUE only if, both the pointers are pointing to the same location. (Which they won't in most cases)
You should be doing if ([mySecondString isEqualToString:myString]), which will be comparing your both string's content for equality.