Getting the size of an array - objective-c

i have some code that requires the use of a for loop to read variables from an array.
int size=sizeof names;
NSLog(#"thelast one is %d",size);
NSString *usersName=userName.text;
NSString *usersPass=passWord.text;
for (i=0; i<=size;i++){
NSString *namesArray=[names objectAtIndex:i];
NSString *passArray=[pass objectAtIndex:i];
NSLog(#"namesArray %#",namesArray);
NSLog(#"passArray %#",passArray);
if([namesArray isEqualToString:usersName]){
userValid=1;
NSLog(#"The content of arry4 is %#",namesArray);
}
if([passArray isEqualToString:usersPass]){
passValid=1;
NSLog(#"The content of arry4 is %#",passArray);
}
else {
userValid=0;
passValid=0;
}
}
I've been having some problems because every time this function is called from within the program, it's almost as if the 'sizeof names' is wrong, therefore not all values in the array are checked.
I'm generally a Java programmer so i'm used to names.length, and i was told sizeof names is essentially the same thing... any help?
Cheers.

Don't use sizeof. Use [names count].

You want to use [names count] not sizeof names. Sizeof is going to give you the size of the actual names object pointer itself and not the number of elements, since it's dynamic memory type.

To get the number of elements stored in an NSAarray you should use the instance method count, which returns an NSUInteger.
Alternatevely, you can iterate over these elements using the for in loop, which is available also in Java, if I recall correctly.
for (MyClass *element in myArray) {
NSLog(#"%#", element);
}
Note that sizeof is a C operator that returns the size in bytes of its operand, so it doesn't tell you how many elements are stored in that NSArray, but the size in bytes of one NSArray instance.

I know your question has already been answered - but here is a more Cocoa way of writing it
NSString *userName = userName.text;
NSString *userPass = passWord.text;
// Use a block enumerator
NSUInteger nameIdx = [names indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return ([obj isEqualToString:userName]);
}];
// Is the name in the array
if (nameIdx == NSNotFound) {
// Name not in array - so set to zero
userValid = 0;
passValid = 0;
} else {
userValid = 1;
// See if the corresponding password is correct
NSString password = [pass objectAtIndex:nameIdx];
if (![password isEqualToString:userPass]) {
passValid = 0;
} else {
passValid = 1;
}

One can also use Fast Enumeration, in some cases it can be more clear to a reader:
NSString *userName = userName.text;
NSString *userPass = passWord.text;
BOOL userValid = NO;
BOOL passValid = NO;
int index = 0;
for (NSString *eachName in namesArray) {
if ([eachName isEqualToString:userName) {
userValid = YES:
if ([[passArray objextAtIndex:index] isEqualToString:passWord) {
passValid = YES;
}
break;
}
index += 1;
}

Related

Matching strings, consider some characters are the same

please help me with this problem.
I want to check if the targetString match the keyword or not. Consider some character may different, but should still return true.
Example:
targetString = #"#ß<"
keyword = #"abc", #"∂B(", #"#Aß<"
result: all must return true.
(Matched.targetString and all keyword are the same.)
Consider me have an array, contains list of character set that can be the same:
NSArray *variants = [NSArray arrayWithObjects:#"aA#∂", #"bBß", #"c©C<(", nil]
So that when matching, with this rule, it can match as the example above.
Here is what i've done so far (using recursion):
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
char c = [keyWord characterAtIndex:i];
NSString *rs = [self variantsWithChar:c];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:[NSString stringWithCharacters:[aString characterAtIndex:i] length:1]].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
- (NSString *) variantsWithChar:(char) c {
for (NSString *s in self.variants) {
if ([s rangeOfString:[NSString stringWithFormat:#"%c",c]].location != NSNotFound) {
return s;
}
}
return [NSString stringWithFormat:#"%c", c];
}
The main problem is, variantsWithChar: doesn't return the correct string. I don't know which datatype and which function should I use here. Please help.
For thou who know ruby, here's the example in ruby. It work super fine!
require 'test/unit/assertions'
include Test::Unit::Assertions
class String
def matching?(keyword)
length >= keyword.length && (keyword.chars.zip(chars).all? { |cs| variants(cs[0]).include?(cs[1]) } || slice(1, length - 1).matching?(keyword))
end
private
VARIANTS = ["aA#∂", "bBß", "c©C<("]
def variants(c)
VARIANTS.find { |cs| cs.include?(c) } || c
end
end
assert "abc".matching?("#ß<")
PS: The fact is, it's containt a japanese character set that sounds the same (like あア, いイ... for thou who know japanese)
PS 2: Please feel free to edit this Question, since my engrish is sooo bad. I may not tell all my thought.
PS 3: And, maybe some may comment about the performance. Like, search about 10,000 target words, with nearly 100 variants, each variant have at most 4 more same characters.
So first off, ignore comments about ASCII and stop using char. NSString and CFString use unichar
If what you really want to do is transpose hiragana and katakana you can do that with CFStringTransform()
It wraps the ICU libraries included in OS X and iOS.
It makes it very simple.
Search for that function and you will find examples of how to use it.
After a while (a day) working on the code above, I finally get it through. But don't know about the performance. Someone comment and help me improve about performance, please. Thanks.
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
NSString* c = [NSString stringWithFormat:#"%C", [keyWord characterAtIndex:i]];
NSString *rs = [self variantsWithChar:c];
NSString *theTargetChar = [NSString stringWithFormat:#"%C", [aString characterAtIndex:i]];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:theTargetChar].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
If you remove all comment, it'll be pretty short...
////////////////////////////////////////
- (NSString *) variantsWithChar:(NSString *) c{
for (NSString *s in self.variants) {
if ([s rangeOfString:c].location != NSNotFound) {
return s;
}
}
return c;
}
You could try comparing ascii values of the japanese characters in the variants's each character's ascii value. These japanese characters aren't treated like usual characters or string. Hence, string functions like rangeOfString won't work on them.
to be more precise: have a look at the following code.
it will search for "∂" in the string "aA#∂"
NSString *string = #"aA#∂";
NSMutableSet *listOfAsciiValuesOfString = [self getListOfAsciiValuesForString:string]; //method definition given below
NSString *charToSearch = #"∂";
NSNumber *ascii = [NSNumber numberWithInt:[charToSearch characterAtIndex:0]];
int countBeforeAdding = [listOfAsciiValuesOfString count],countAfterAdding = 0;
[listOfAsciiValuesOfString addObject:ascii];
countAfterAdding = [listOfAsciiValuesOfString count];
if(countAfterAdding == countBeforeAdding){ //element found
NSLog(#"element exists"); //return string
}else{
NSLog(#"Doesnt exists"); //return char
}
===================================
-(NSMutableSet*)getListOfAsciiValuesForString:(NSString*)string{
NSMutableSet *set = [[NSMutableSet alloc] init];
for(int i=0;i<[string length];i++){
NSNumber *ascii = [NSNumber numberWithInt:[string characterAtIndex:i]];
[set addObject:ascii];
}
return set;
}

Efficient way of checking the content of every NSDictionary in NSArray

In my app I'me getting responses from the server and I have to check that I don't create duplicate objects in the NSArray which contains NSDictionaries. Now to check if the objects exists I do this:
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
}
if (i == appDelegate.currentUser.userSiteDetailsArray.count - 1 && ![[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier] && needToCheck){
// It means it's the last object we've iterated through and needToCheck is still = YES;
//Doing stuff here
}
}
I set up a BOOL value because this iteration goes numerous times inside a method and I can't use return to stop it. I think there is a better way to perform this check and I would like to hear your suggestions about it.
BOOL needToCheck = YES;
for (int i = 0; i < appDelegate.currentUser.userSiteDetailsArray.count; i++){
NSDictionary *tmpDictionary = [appDelegate.currentUser.userSiteDetailsArray objectAtIndex:i];
if ([[tmpDictionary valueForKey:#"webpropID"] isEqualToString:tmpWebproperty.identifier]){
needToCheck = NO;
break;
}
}
if (needToCheck) {
//Doing stuff here
}
But, as others have said, you can maybe keep a "summary" in a separate NSSet that you check first, vs spinning through all the dictionaries.
NSDictionary *previousThing = nil;
for (NSDictionary *thing in appDelegate.currentUser.userSiteDetailsArray) {
if ([thing[#"webpropID"] isEqualToString:newWebPropertyIdentifier]) {
previousThing = thing;
break;
}
}
if (previousThing == nil) {
// no previous thing
} else {
// duplicate
}

Search String in NSDictionary store in NSMutableArray

I am trying to search a String in NSDictionary stored in NSMutableArray
for (int k = 0; k < [onlyActiveArr count]; k++) {
NSString *localID = [onlyActiveArr objectAtIndex:k];
NSLog(#"%#",localID);
int localIndex = [onlyActiveArr indexOfObject:localActiveCallID];
NSLog(#"%d",localIndex);
for (NSDictionary *dict in self.SummaryArr) {
NSLog(#"%#",[dict objectForKey:#"ActiveID"]);
if (![[dict objectForKey:#"ActiveID"] isEqualToString:localID]) {
NSLog(#"found such a key, e.g. %#",localID);
}
}
}
But I am getting
NSLog(#"found such a key, e.g. %#",localActiveCallID);
when the ID is still there in SummaryArr, I am checking if localID retrieved from onlyActiveArr is not present in dictionary.
Please suggest me how to overcome my problem.
You cannot make a decision that a key is not present until you finish processing the entire dictionary. Make a boolean variable initially set to NO, and change it to YES if you find an item in the dictionary, like this:
BOOL found = NO;
for (NSDictionary *dict in self.SummaryArr) {
NSLog(#"%#",[dict objectForKey:#"ActiveID"]);
found = [[dict objectForKey:#"ActiveID"] isEqualToString:localID];
if (found) break;
}
if (!found) {
NSLog(#"found such a key, e.g. %#",localID);
}
If you like predicates, then you can use the fact that accessing an inexistent key in a dictionary produces a nil value and make a predicate that filters out those dictionaries that have nil for your key.
If the count of the result is larger than zero, your key is somewhere in the array. It won't tell you where, though.
A snippet to show the idea:
NSPredicate *keyPred = [NSPredicate predicateWithFormat: #"NOT ActiveID == nil"];
BOOL found = [[self.SummaryArr filteredArrayUsingPredicate: keyPred] count] > 0;
I don't know how the performance stacks up, so if your data set is large you may want to check that the execution time is within your limits.

Count equal objects in NSArray

I've been trying to figure out a way of checking how many of a certain object are in an NSArray.
I've looked through the docs and I'm pretty sure there is no premade method for this. Also I can't find anything here on SO.
Do anybody know about a good way to do this? Because I seriously can't come up with anything.
In this specific case I have an array with strings (most cases several of each) and I want to count how many strings in the array that matches to whatever I ask for.
If this is a primary use of the data structure and order doesn't matter, consider switching to an NSCountedSet which is specifically for solving this problem efficiently.
If you need an ordered collection, and you don't have a huge set of objects, than the fast enumeration answers are the best approach.
If you want to know where the objects are, then use indexesOfObjectsPassingTest:.
If you have a huge number of object, I would look at indexesOfObjectsWithOptions:passingTest: with the NSEnumerationConcurrent option. This will allow you to search the array on multiple cores. (This is only possibly faster on a multi-core device, and even then is probably only faster if you have a very large collection. You should absolutely test before assuming that concurrent will be faster.) Even if you just need the final count, it may be faster for certain data sets to use this method and then use count on the final index set.
There actually is a method for this: - (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:^(id obj, NSUInteger index, BOOL *stop) {
return [obj isEqualTo:myOtherObject];
}];
Sounds like a case for NSCountedSet, which does what you are after with its initWithArray: initializer:
// Example array of strings
NSArray *array = [NSArray arrayWithObjects:
#"Joe", #"Jane", #"Peter", #"Paul",
#"Joe", #"Peter", #"Paul",
#"Joe",
#"Jane", #"Peter",
nil];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray: array];
// for-in will let you loop over the counted set
for (NSString *str in countedSet) {
NSLog(#"Count of %#: %ld", str, (long)[countedSet countForObject:str]);
}
One approach would be to iterate and check.
- (int)repeatsOf:(NSString *)repeater inArray:(NSArray *)array {
int count = 0;
for (NSString *item in array) {
if ([item isEqualToString:repeater]) {
count++;
}
}
return count;
}
You could try a simple loop. Suppose needle is your reference string and array is your NSArray of strings:
unsigned int n = 0;
for (NSString * str in array)
{
if ([needle isEqualToString:str])
{
++n;
}
}
Now n holds the count of strings in equal to needle.
You could define a function like this:
- (int)countStringsThatMatch:(NSString*)match inArray:(NSArray*)array
{
int matches = 0;
for (id string in array) {
if ([string isEqualToString:match]) {
matches++;
}
}
return matches;
}
And then use it like:
int count = [self countStringsThatMatch:#"someString" inArray:someArray];
- (NSUInteger) objectCountInArray:(NSArray *)array
matchingString:(NSString *)stringToMatch {
NSUInteger count = 0;
for (NSString *string in array) {
count += [string isEqualToString:stringToMatch] ? 1 : 0;
}
return count;
}
You can try to expand this to use a block that gets an object and returns a BOOL. Then you can use it to compare an array of whatever you want.

Works with number of string contained in NSArray

I need to pick string valur from an NSMutableArray then save it into a plist. I've builded an NSMutableArray to display infos in table View. Maximum allowed index is 8. (paste just two in example)
The problem if the String doesn't exist, I get the following error:
sDict is a dictionary for saving datas to a property list file.
the code:
- (IBAction)save:(id)sender {
(...)
NSString *One;
NSString *Two;
...etc
if ([self.smOne objectAtIndex:0])
One = [self.smOne objectAtIndex:0];
if ([self.smOne objectAtIndex:1])
Two = [self.smOne objectAtIndex:1];
...etc
if (One)
[sDict setObject:[self.smTwo objectAtIndex:0]
forKey:[UserM stringByAppendingString:One]];
[sDict setObject:[self.smThree objectAtIndex:0]
forKey:[UserS stringByAppendingString:One]];
[sDict setObject:[self.smFour objectAtIndex:0]
forKey:[UserP stringByAppendingString:One]];
if (Two)
[sDict setObject:[self.smTwo objectAtIndex:1]
forKey:[UserM stringByAppendingString:Two]];
[sDict setObject:[self.smThree objectAtIndex:1]
forKey:[UserS stringByAppendingString:Two]];
[sDict setObject:[self.smFour objectAtIndex:1]
forKey:[UserParM stringByAppendingString:Two]];
...etc
}
This code works if all objects are present, but fails if it miss one of the object at index.
I really don't know how to check properly if the object is present or not, cause code above seem's to don't works well.
I've tried with [self.smOne count] but as problem to pass as a Int or String to make conditions with.
Thanks for answer.
it looks like you're explicitly checking smOne from indices 1 through 8. But you also mentioned that the array can have up to 8. So if it's missing, say, 6, 7 and 8, you'd still be calling [smOne objectAtIndex:6], which would result in an NSRangeException being raised as 6 is out of bounds for the array.
try this instead:
int i = 0;
for ( NSString *aString in self.smOne )
{
[sDict setObject:[self.smTwo objectAtIndex:i]
forKey:[UserM stringByAppendingSting:aString]];
[sDict setObject:[self.smThree objectAtIndex:i]
forKey:[UserS stringByAppendingString:aString]];
[sDict setObject:[self.smFour objectAtIndex:i]
forKey:[UserP stringByAppendingString:aString]];
i++;
}
it'll go through each object in the smOne array and add the object into sDict regardless of how many items you have in smOne.
also, be careful with how you're generating your keys. there's the possibility that [UserM stringByAppendingSting:aString] won't always be unique.
Sorry to ask again but i have dificulties to find how to rebuild arrays from the key/string couple saved with the loop.
i've tried this:
int i = 0;
for (NSString *SMServ in [temp objectForKey:
[UserMen stringByAppendingFormat:#"%d",i]]){
NSString *SMMem[i];
SMMem[i] = [temp objectForKey:[UserMen stringByAppendingFormat:#"%d",i]];
NSArray *theArray = [ NSArray arrayWithObjects:SMMem count:i];
i++;
}
But nothing happens.
if i try with for (i = 0; i < 9; i ++) and (i + 1) instead of [i], i get same bounds errors in the first example.
thanks again for help.
Well, finally it ,works. Thanks for link to the documentation, i read too fast last time.
I'm sure this is not the cleanest way, but it works with this loop:
for (key in sDict) {
if ([sDict valueForKey:[UserMen stringByAppendingFormat:#"%d",i]]) {
tempMe = [sDict valueForKey:[UserMen stringByAppendingFormat:#"%d",i]];
if (tempMe) {
[manArray insertObject:tempMe atIndex:(0 + a)];
[bankArray insertObject:[NSString stringWithFormat:#"%d",i] atIndex:(0 + a)];
a++;
}
}
if ([sDict valueForKey:[UserSerial stringByAppendingFormat:#"%d",i]]) {
SMSer = [sDict valueForKey:[UserSerial stringByAppendingFormat:#"%d",i]];
if (SMSer) {
[serArray insertObject:SMSer atIndex:(0 + b)];
b++;
}
}
if ([sDict valueForKey:[UserPart stringByAppendingFormat:#"%d",i]]) {
SMPart = [sDict valueForKey:[UserPart stringByAppendingFormat:#"%d",i]];
if (SMPart) {
[partArray insertObject:SMPart atIndex:(0 + c)];
c++;
}
}
i++;
}