Returning the number of objectForKey - iphone

I have a part of my code that returns the numberOfRowsInSection.
Code
for (NSDictionary *consoleDictionary in [self arrayFromJSON]) {
if ([[consoleDictionary objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%#", consoleDictionary);
}
}
Output
2013-02-03 22:37:08.468 PageControl01[5782:c07] {
console = PlayStation;
game = "007 Legends";
id = 1;
model = "PlayStation 3";
publisher = "Electronic Arts";
}
2013-02-03 22:37:08.478 PageControl01[5782:c07] {
console = PlayStation;
game = "Ace Combat: Assault Horizon";
id = 2;
model = "PlayStation 3";
publisher = Namco;
}
This one is apparently right because it logs all the "PlayStation 3" model. However, this is not what I need. I want to log the number of "PlayStation 3"'s. So I tweak the code a little bit and then this:
for (NSDictionary *consoleDictionary in [self arrayFromJSON]) {
if ([[consoleDictionary objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", [consoleDictionary count]);
}
}
Output
2013-02-03 22:39:43.605 PageControl01[5816:c07] 5
2013-02-03 22:39:43.605 PageControl01[5816:c07] 5
This one is pretty near yet so close. Instead of logging the number 5, it should log the number 2 since there are only 2 "PlayStation 3".
Please help.

You don't need to explicitly loop through the array.
NSIndexSet *is = [array indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[obj objectForKey:#"model"] isEqualToString:#"PlayStation 3"];
}];
int numOfPS3s = is.count;

It is logging the number 5 because there is 5 keys in each of your dictionaries (console, game, id, model, publisher). If instead of logging the [consoleDictionary count] you simply add one to an int counter each time, you would get the expected result in your counter at the end.
You can obtain the number of objects is a much more easier way: [self arrayFromJSON] is an array
Typically:
NSInteger nbPS3 = [[self arrayFromJSON] indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [obj[#"model"] isEqualToString:#"PlayStation 3"];
}].count

Yes, count is always the total count, not the index, not the count of matches. Using indexesOfObjectsPassingTest is the most direct solution for this specific question, but if you're interested in other techniques for iterating through your result set, but also keeping track of not only the object, but also the index, consider these two approaches, too:
for (NSInteger i = 0; i < [self.arrayFromJSON count]; i++) {
if ([[[self.arrayFromJSON objectAtIndex:i] objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", i);
}
}
or
[self.arrayFromJSON enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj objectForKey:#"model"] isEqualToString:#"PlayStation 3"]) {
NSLog(#"%d", idx);
}
}];
Obviously, if you're looking for how many records match, you'd just increment your own counter, rather than logging the index for each match.

Related

indexOfObject in an NSMutableArray returning garbage value

Trying to get index on an object in NSMutableArray. It is returning some garbage value, not getting why it is not returning index of particular item. Below is code i tried.
NSString *type = [dictRow valueForKey:#"type"];
if([arrSeatSel indexOfObject:type])
{
NSUInteger ind = [arrSeatSel indexOfObject:type];
[arrTotRows addObject:[arrSeatSel objectAtIndex:ind]];
}
type contains value "Gold". And arrSeatSel contains
(
"Gold:0",
"Silver:0",
"Bronze:1"
How to check that. Please guide.
The value you are getting is NSNotFound. You are getting NSNotFound because #"Gold" is not equal to #"Gold:0".
You should try the following
NSUInteger index = [arrSeatSel indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
return [obj hasPrefix:type];
}];
if (index != NSNotFound) {
[arrTotRows addObject:[arrSeatSel objectAtIndex:index]];
}
UPDATE
-indexOfObjectPassingTest: is a running the following loop. NOTE: /* TEST */ is some code that returns true when the correct index is found.
NSUInteger index = NSNotFound;
for (NSUInteger i = 0; i < [array count]; ++i) {
if (/* TEST */) {
index = i;
break;
}
}
In my first sample, /* TEST */ is [obj hasPrefix:type]. The final for loop would look like.
NSUInteger index = NSNotFound;
for (NSUInteger i = 0; i < [arrSeatSel count]; ++i) {
if ([arrSeatSel[i] hasPrefix:type]) {
index = i;
break;
}
}
if (index != NSNotFound) {
[arrTotRows addObject:[arrSeatSel objectAtIndex:index]];
}
I like -indexOfObjectPassingTest: better.
The [obj hasPrefix:type] part is just a different way to comparing strings. Read the -hasPrefix: doc for more details.
Hope that answers all your questions.
Sometimes storing data properly can solve lot of hazzle. If I guess correctly
"Gold:0" denotes a circle of type Gold an its count 0.
You can try to reformat that to an array of items.
Such as
[
{
"Type": "Gold",
"Count": 0
},
{
"Type": "Silver",
"Count": 0
},
{
"Type": "Bronze",
"Count": 1
}
]
And then using predicate to find the index
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Type == %#",#"Gold"];
NSUInteger index = [types indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
[predicate evaluateWithObject:obj];
}];
You can try doing this.
[arrSeatSel enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// object - will be your "type"
// idx - will be the index of your type.
}];
hope this helps.
If I'm reading that correctly, you're saying that arrSeatSel contains three NSStrings, #"Gold:0", #"Silver:0", and #"Bronze:1", correct?
And then your NSString* type is basically #"Gold"
The first thing is Gold and Gold:0 are different strings, and that's just for starter.
As you are searching for a string in the array, you should take each string out, and do a string matching, not just a comparison. What I'm saying is this:
NSString* str1 = #"This is a string";
NSString* str2 = #"This is a string";
if ( str1 == str 2 ) NSLog(#"Miracle does happen!")
The conditional would never be true even though both NSStrings contain the same value, they are different object, thus are different pointers pointing to different blocks of memory.
What you should do here is a string matching, I will recommend NSString's hasPrefix: method here, as it seems to fit your need.

How to get index in an NSArray?

NSMutableArray*array = [[NSMutableArray alloc]init];
NSArray*Somearray = [NSArray arrayWithObjects:1st Object,2ndObject,3rd Object,4th object,5th Object,nil];
In the above array 1st Object,2ndObject,3rd Object,4th object,5th Object having val,content,conclusion in each index.
for(int i=0;i<[Somearray count];i++)
{
______________
Here the code is there to give each index ,that is having val,content,conclusion ..
After that val,content,conclusion in each index will be add to Dict..
____________
NSDictionary *Dict = [NSDictionary dictionaryWithObjectsAndKeys:val,#"val",content,#"content",conclusion,#"conclusion",nil];
//Each time adding dictionary into array;
[array addObject:Dict];
}
The above Dictionary is in for loop and the keyvalue pairs will be add 5 times(Somearray Count).Now array is having in
array = [{val="1.1 this is first one",content="This is the content of 0th index",conclusion="this is the conclusion of 0th index"},{val="1.2 this is first one",content="This is the content of 1st index",conclusion="this is the conclusion of 1st index"},____,____,______,{val="1.5 this is first one",content="This is the content of 4th index",conclusion="this is the conclusion of 4th index"},nil];
Now i am having NSString*string = #"1.5";
Now i need the index where val is having 1.5 in it.How to send the str in to array to find the the index.
Can anyone share the code please.
Thanks in advance.
Use method indexOfObject
int inx= [array indexOfObject:#"1.5"];
For Find index particular key value.
int inx;
for (int i=0; i<[array count]; i++) {
if ([[[array objectAtIndex:i] allKeys] containsObject:#"val"]) {
inx=i;
break;
}
}
The method you are looking for is -[NSArray indexOfObjectPassingTest:]. You would use it like this:
NSUInteger i = [array indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [[id objectForKey:#"val"] rangeOfString:#"1.5"].location != NSNotFound;
}];
If you just want to check that val starts with "1.5" you would use hasPrefix: instead.
Try this -
NSArray *valArray = [array valueForKey:#"val"];
int index = [valArray indexOfObject:#"1.5"];
Appended answer given by Mandeep, to show you the magic of key value coding ;)
NSUInteger idx = UINT_MAX;
NSCharacterSet* spaceSet = [NSCharacterSet whitespaceCharacterSet];
for(int i=0,i_l=[Yourarray count];i<i_l;i++) {
NSString* s_prime = [[Yourarray objectAtIndex:i] valueForKey:#"val"];
if ([s_prime length] < 4) {
continue;
}
NSString *subString = [[s_prime substringToIndex:4] stringByTrimmingCharactersInSet:spaceSet];
// NSLog(#"index %#",s);
if ([subString isEqualToString:secretNumber]){
idx = i;
break;
}
}
if (idx != UINT_MAX) {
// NSLog(#"Found at index: %d",idx);
} else {
// NSLog(#"Not found");
}

endless "for" loop in release configuration of iPhone app

i have the following code in my application:
NSArray *lstAttributes = [conditionAttribute componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValues = [conditionValue componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValueTypes = [conditionValueType componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator];
if([lstAttributes count] != [lstValues count] ||
[lstAttributes count] != [lstValueTypes count]) return NO;
BOOL bResult = YES;
NSLog(#"attributes amount - %u", [lstAttributes count]);
for(uint i = 0; i < [lstAttributes count]; i ++)
{
NSLog(#"counter: %u", i);
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:i];
condition.conditionValue = [lstValues objectAtIndex:i];
condition.conditionValueType = [lstValueTypes objectAtIndex:i];
bResult &= [self checkCondition:condition forOwner:owner];
FreeObject(&condition);
if(!bResult) break;
}
return bResult;
everything is fine in "debug" configuration. but once i switch it to "release" i face the endless loop. console shows me the following: attributes amount - 2, counter: 0, counter: 1, counter: 1, counter: 1, counter: 1, counter: 1, counter: 1, ...... etc.
i tried to use different "for" and "while" operators, but nothing of that worked correctly. the loop still can't be stopped.
has anyone faced the same problem before?
Since you mention in your comments that it works if you comment out the &= operation, I'd change that line to:
bResult = bResult && [self checkCondition:condition forOwner:owner];
This does a boolean AND, which is what you really intend to do on this line anyway. &= does a bitwise AND, which isn't always equivalent to a boolean AND, since any nonzero value evaluates to true. For example:
BOOL a = 1;
BOOL b = 2;
if (a && b) {
NSLog (#"a && b is true"); // This line will execute
}
if (a & b) {
NSLog (#"a & b is true"); // This line won't execute
}
Since it's common to use object addresses in our conditionals, using the bitwise AND in place of a boolean AND could create bugs. For example, if you return an object address from getCondition:fromOwner:, intending it to mean YES, and that memory address happens to be an even number, then a bitwise AND with YES will evaluate to NO, where a boolean AND would evaluate to YES.
My best guess as to the cause of your specific bug is that the bitwise-AND is somehow causing a buffer overrun, which stomps on your i variable. If that hypothesis is correct, then switching to a boolean AND should fix that too.
workaround for this bug as promised:
at first i tried to use code blocks to enumerate all objects in a list instead of "for" loop.
__block BOOL bResult = YES;
[lstAttributes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:idx];
condition.conditionValue = [lstValues objectAtIndex:idx];
condition.conditionValueType = [lstValueTypes objectAtIndex:idx];
bResult &= [self checkCondition:condition forOwner:owner];
FreeObject(&condition);
if(!bResult) *stop = YES;
}];
but i got a crash on second iteration. "lstValues" and "lstValueTypes" pointers suddenly changed their values and application received EXC_BAD_ACCESS. possibly usage of 3 arrays while enumerating only 1 of them is not a good idea. debugger shows that enumeration is performed on the same thread, but 2 of 3 arrays are corrupted by the moment of 2nd iteration.
so i decided to split up my initial loop into 2 parts:
prepare a list of conditions
check each condition.
first is a usual "for" loop, second - 'enumerateObjectsUsingBlock:" method of NSArray class. so the final code looks like:
NSArray *lstAttributes = [conditionAttribute componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValues = [conditionValue componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator],
*lstValueTypes = [conditionValueType componentsSeparatedByString:cstrSystemUIConfigurationAttributeConditionSeparator];
if([lstAttributes count] != [lstValues count] ||
[lstAttributes count] != [lstValueTypes count]) return NO;
NSMutableArray *lstConditions = [NSMutableArray new];
for(uint i = 0; i < [lstAttributes count]; i ++)
{
SystemUIConfigurationAttributeCondition *condition = [SystemUIConfigurationAttributeCondition new];
condition.conditionAttribute = [lstAttributes objectAtIndex:i];
condition.conditionValue = [lstValues objectAtIndex:i];
condition.conditionValueType = [lstValueTypes objectAtIndex:i];
[lstConditions addObject:condition];
FreeObject(&condition);
}
__block BOOL bResult = YES;
[lstConditions enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL *stop)
{
if([self checkCondition:[lstConditions objectAtIndex:i] forOwner:owner] == NO)
{
bResult = NO;
*stop = YES;
}
}];
FreeObject(&lstConditions);
return bResult;
this code works.
i would appreciate if anyone can explain the behavior of my initial loop.

get values form NSMutableArray from end to start

I want to take the values from NSMutableArray but want to read from last index to 1st index
thank you
for (id someObject in [someArray reverseObjectEnumerator])
{
//do your thing
}
2 other options:
Simple for-loop (surely not recommended):
for (int i = [array count]-1; i >= 0; ++i)
id value = [array objectAtIndex: i];
Block-based enumeration:
[array enumerateObjectsWithOptions: NSEnumerationReverse
usingBlock: ^(id obj, NSUInteger idx, BOOL *stop){
//do something
}];
Mark's answer is useful, but this form may be useful when you want to mutate the array:
while ([arr count]) {
id obj = [arr lastObject];
// use obj
[arr removeLastObject];
}

Gather the count of a specific object from NSMutableArray

Hey guys & girls,
Im wondering how I can find the object count of a specific type of object in an array.
For example, i have 6 'clouds' in NSMutableArray at random locations, I also have 4 'dragons' in this NSMutableArray.
How can i gather the integer 6?
I was thinking something along the lines of:
int z = [[SomeClass *clouds in _somearray] count];
Any assistance would be greatly appreciated.
Thnx,
Oliver.
Yet another way is in using blocks:
Class cloadClass = NSClassFromString(#"Cloud");
NSArray *a = /* you array with clouds and dragons */;
NSIndexSet *clouds = [a indexesOfObjectsPassingTest:
^(id obj, NSUInteger idx, BOOL *stop) {
return [obj isKindOfClass:cloadClass];
}];
// now we can count clouds
NSLog(#"%d", [clouds count]);
// but also we now can return our clouds immediately and
NSLog(#"%#", [a objectsAtIndexes:clouds]);
int result = 0;
for (NSObject *object in _somearray) {
if ([object isKindOfClass:[SomeClass class]])
result++;
}
result is the count you are looking for
If you're looking for how many times a specific instance of an object appears, you can do:
NSCountedSet *counts = [NSCountedSet setWithArray:myArrayOfObjects];
NSUInteger count = [counts countForObject:myObject];
Otherwise you'd just have to loop through the array manually and count.