I have been trying to get this to work for quite some time now and is somewhat running out of options. I am trying to use "allKeysForObject" to select all keys for a certain object.
This is test code to try to select the keys for a result. The objects in the dictionary is suppose to be numbers but when i want to display it i am using %# to get the result, which for me indicate that this is not number.
I have been playing around to try to select the key with int, NSString (as in the code example) and used the allObject array to do the select but have not been able to succeed. As i am very new at this i am running out of option and have to reach out for help.
NSDictionary *playerResultInTheGame = [readCurrentGameDataFunction finalResultForCurrentGame];
NSLog(#"playerResultInTheGame: %#", playerResultInTheGame);
NSArray *allPlayers = [playerResultInTheGame allKeys];
NSArray *allObjects = [playerResultInTheGame allValues];
NSLog(#"allObjects: %#", allObjects);
NSMutableArray *myObjectsArray = [[NSMutableArray alloc] init];
allObjects = [allObjects sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"allObjects: %#", allObjects);
NSArray *xxxxx = [playerResultInTheGame allKeysForObject:#"1"];
NSLog(#"xxxxx: %#", xxxxx);
The result i get is:
2011-01-17 20:50:34.554 XX[11203:207] playerResultInTheGame: {
Barnspelare = 2;
Vuxenspelare = 1;
}
2011-01-17 20:50:34.554 XX[11203:207] allObjects: (
2,
1
)
2011-01-17 20:50:34.555 XX[11203:207] allObjects: (
1,
2
)
Current language: auto; currently objective-c
2011-01-17 20:51:50.086 XX[11203:207] xxxxx: (
)
2011-01-17 20:52:24.523 XX[11203:207] allPlayers: (
Barnspelare,
Vuxenspelare
)
Okay, so if you have a list of players, as the keys, and their scores as objects, to get that list printed at all, you might do this:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"1", #"player1", #"2", #"Player2", #"3", #"player3", nil];
NSLog(#"%#", [dict objectForKey:#"player1"]);
NSString *key;
for (key in dict) {
NSLog(#"Player: %#, Result: %#", key, [dict objectForKey:key]);
}
[pool drain];
return 0;
}
The keys are probably numbers rather than strings. Using an int is definitely wrong, because Cocoa collections cannot store ints. You have to use NSNumber. So use [playerResultInTheGame allKeysForObject:[NSNumber numberWithInt:1]].
Related
I have an iPad that reads data from an SQL database. The following code works fine and retrieves 2 fields from each record and reads them into an NSArray.
I now need to read 5 of the fields and I can't help but think that there is a better way of doing it rather than running 5 separate queries through php (the getinfo.php file with the choice parameter set to pick the different fields).
Any pointers to a better method for doing this?
NSString *strURLClass = [NSString stringWithFormat:#"%#%#", #"http://wwwaddress/getinfo.php?choice=1&schoolname=",obsSchoolName];
NSArray *observationsArrayClass = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:strURLClass]];
observationListFromSQL = [[NSMutableArray alloc]init];
NSEnumerator *enumForObsClass = [observationsArrayClass objectEnumerator];
NSString *strURLDate = [NSString stringWithFormat:#"%#%#", #"http://wwwaddress/getinfo.php?choice=5&schoolname=",obsSchoolName];
NSArray *observationsArrayDate = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:strURLDate]];
observationListFromSQL = [[NSMutableArray alloc]init];
NSEnumerator *enumForObsDate = [observationsArrayDate objectEnumerator];
id className, dateOfObs;
while (className = [enumForObsClass nextObject])
{
dateOfObs = [enumForObsDate nextObject];
[observationListFromSQL addObject:[NSDictionary dictionaryWithObjectsAndKeys:className, #"obsClass", dateOfObs, #"obsDate",nil]];
}
Yes, you can do that with less code by "folding" the statements into a loop, and using a mutable dictionary:
// Add other items that you wish to retrieve to the two arrays below:
NSArray *keys = #[#"obsClass", #"obsDate"]; // Key in the dictionary
NSArray *choices = #[#1, #5]; // Choice in the URL string
NSMutableArray *res = [NSMutableArray array];
NSMutableArray *observationListFromSQL = [NSMutableArray array];
for (int i = 0 ; i != keys.count ; i++) {
NSNumber *choice = choices[i];
NSString *strURLClass = [NSString stringWithFormat:#"http://wwwaddress/getinfo.php?choice=%#&schoolname=%#", choice, obsSchoolName];
NSArray *observationsArray = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:strURLClass]];
NSEnumerator *objEnum = [observationsArrayClass objectEnumerator];
NSString *key = keys[i];
NSMutableDictionary *dict;
if (res.count < i) {
dict = res[i];
} else {
dict = [NSMutableDictionary dictionary];
[res addObject:dict];
}
id item;
while (item = [objEnum nextObject]) {
[res setObject:item forKey:key];
}
}
I have two NSArray objects that I would like to be sorted the same. One contains NSString objects, the other custom Attribute objects. Here is what my "key" NSArray looks like:
// The master order
NSArray *stringOrder = [NSArray arrayWithObjects:#"12", #"10", #"2", nil];
The NSArray with custom objects:
// The array of custom Attribute objects that I want sorted by the stringOrder array
NSMutableArray *items = [[NSMutableArray alloc] init];
Attribute *attribute = nil;
attribute = [[Attribute alloc] init];
attribute.assetID = #"10";
[items addObject:attribute];
attribute = [[Attribute alloc] init];
attribute.assetID = #"12";
[items addObject:attribute];
attribute = [[Attribute alloc] init];
attribute.assetID = #"2";
[items addObject:attribute];
So, what I would like to do is use the stringOrder array to determine the sorting of the items array of custom objects.
How can I do this?
Hereby, I compare directly the index of obj1.assetID in stringOrder with the index of obj2.assetID in stringOrder (using Objective-C literals for #() to transform NSString => NSNumber)
[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
return [#([stringOrder indexOfObject:obj1.assetID]) compare:#([stringOrder indexOfObject:obj2.assetID])]
}];
Or without ObjC literals :
[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
return [[NSNumber numberWithInt:[stringOrder indexOfObject:obj1.assetID]] compare:[NSNumber numberWithInt:[stringOrder indexOfObject:obj2.assetID]]]
}];
While cwehrungs answer will get the job done, the performance is not great on relatively small arrays.
Here is another method for performing the same kind of sort that is a bit quicker (though still far from perfect):
NSMutableArray *sorted = [NSMutableArray array];
// pre-populate with objects
for (int i = 0; i < stringOrder.count; i++)
{
[sorted addObject:[NSNull null]];
}
// place the items at the correct position
for (Attribute *a in items)
{
NSUInteger idx = [stringOrder indexOfObject:a.assetID];
if (idx != NSNotFound)
{
[sorted setObject:a atIndexedSubscript:idx];
}
}
// finally remove all the unecesarry placeholders if one array was smaller
[sorted removeObject:[NSNull null]];
Comparison
Here are the results form running the two methods on an iPhone 5:
sortUsingComparator:
100 - 0.012 s
1000 - 1.116 s
2000 - 4.405 s
3000 - 9.028 s
prepopulated array
100 - 0.003 s
1000 - 0.236 s
2000 - 0.917 s
3000 - 2.063 s
There are a couple approaches you could take.
You could store your Attribute objects in an NSDictionary, with the keys being the strings in your stringOrder array. Then, you could get a sorted array of the keys and use that to populate whatever view you're using to display them:
NSArray* sortedKeys = [dict keysSortedByValueUsingComparator:^(id obj1, id obj2) {
return [obj1 compareTo:obj2];
}
The other is that you make the sort order an intrinsic property of your Attribute object, so an array of Attributes can be sorted directly. I would only recommend taking this approach if the sort order is actually an intrinsic property of your Attributes object. If it isn't and you do this, you'll wind up storing presentation information where it doesn't belong.
Here's an example:
NSArray* sortedAttrs = [attributes sortedArrayUsingComparator:^(id obj1, id obj2) {
// Perform comparison of Attribute's, ahem, attributes
}
Here is the solution that I came up with that works extremely well. Anyone see performance issues with this?
for (Attribute *a in items) {
int index = [stringOrder indexOfObject:a.assetID];
a.sortOrder = index;
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [items sortedArrayUsingDescriptors:sortDescriptors];
Parallel Processing:
Results (quad core):
1. sortme:95 sortby:852345 sorted:95 time:0.052576
2. sortme:54248 sortby:852345 sorted:54243 time:0.264660
-(NSArray *)sortArray:(NSArray *)sortme sortBy:(NSArray *)sortBy{
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
NSSet *sortmeSet = [NSSet setWithArray:sortme];
NSMutableDictionary *sortDictionary = [NSMutableDictionary dictionary];
dispatch_queue_t sortDictionaryThread = dispatch_queue_create("my.sortDictionaryThread", DISPATCH_QUEUE_CONCURRENT);
[sortBy enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([sortmeSet containsObject:obj]){
dispatch_barrier_async(sortDictionaryThread, ^{
sortDictionary[obj] = #(idx);
});
}
}];
__block NSArray *sortedArray = nil;
dispatch_barrier_sync(sortDictionaryThread, ^{
sortedArray = [sortDictionary keysSortedByValueUsingSelector:#selector(compare:)];
});
NSLog(#"sortme:%li sortby:%li sorted:%li time:%f",sortme.count,sortBy.count,sortedArray.count, CFAbsoluteTimeGetCurrent() - time);
return sortedArray;
}
I need to run a query that looks would look like
INSERT INTO Appointments (field1, field2, field3, ..., field30) VALUES (value1, value2, value3, ..., value30)
I have my Appointments being stored inside a Dictionary and would like to loop through that dictionary to make the keys equal the fields and the values equal the values.
I'm trying to use the executeUpdate:... withParameterDictionary:... but can't figure out how to make that work with multiple fields if I don't know the field names. The field names are being sent via JSON and instead of manually typing out 30 fields I would just like to loop through the dictionary and get them that way.
I have even tried
NSMutableArray *keys = nil;
NSMutableArray *values = nil;
for (NSDictionary *dict in [json objectForKey:#"data"]) {
keys = [NSMutableArray array];
values = [NSMutableArray array];
for (id key in dict) {
[keys addObject:key];
[values addObject:[NSString stringWithFormat:#":%#", key]];
}
NSString *keyString = [keys componentsJoinedByString:#","];
NSString *valueString = [values componentsJoinedByString:#","];
[[dataObj db] executeUpdate:#"DELETE FROM Appointments"];
NSLog(#"INSERT INTO Appointments (%#) VALUES (%#)", keyString, valueString);
[[dataObj db] executeUpdate:#"INSERT INTO Appointments (?) VALUES (?)", keyString, valueString];
}
The code above prints the NSLog how the query should looks but nothing is being inserted into the database. I know this because I am opening the simulator database file after the queries run and it is still blank.
How can I get the above code to work or how can I get the executeQuery:... withParameterDictionary:... to work with multiple names.
I ran a couple of quick tests, and this works for me:
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:#"AAAA44", #"a", #"BBBB44", #"b", #"CCCC44", #"c", nil];
NSMutableArray* cols = [[NSMutableArray alloc] init];
NSMutableArray* vals = [[NSMutableArray alloc] init];
for (id key in dict) {
[cols addObject:key];
[vals addObject:[dict objectForKey:key]];
}
NSMutableArray* newCols = [[NSMutableArray alloc] init];
NSMutableArray* newVals = [[NSMutableArray alloc] init];
for (int i = 0; i<[cols count]; i++) {
[newCols addObject:[NSString stringWithFormat:#"'%#'", [cols objectAtIndex:i]]];
[newVals addObject:[NSString stringWithFormat:#"'%#'", [vals objectAtIndex:i]]];
}
NSString* sql = [NSString stringWithFormat:#"insert into test (%#) values (%#)", [newCols componentsJoinedByString:#", "], [newVals componentsJoinedByString:#", "]];
NSLog(#"%#", sql);
BOOL updateSuccess = [db executeUpdate:sql];
The trick is to add ' to the data in the arrays.
NSDictionary *argsDict
= [NSDictionary dictionaryWithObjectsAndKeys:#"My Name",
#"name", nil];
[db executeUpdate:#"INSERT INTO myTable (name) VALUES (:name)"
withParameterDictionary:argsDict];
Here is some sample code I just wrote to support optional values at insert time. Just briefly tested but I think it works.
NSMutableDictionary* fieldsandvalues = [NSMutableDictionary dictionary];
fieldsandvalues[#"word"] = userphrase.word;
fieldsandvalues[#"translation"] = userphrase.translation;
if (userphrase.samplesentence.length > 0) {
fieldsandvalues[#"samplesentence"] = userphrase.samplesentence;
}
if (userphrase.notes.length > 0) {
fieldsandvalues[#"notes"] = userphrase.notes;
}
NSMutableArray* keyswithcolon = [NSMutableArray array];
for (NSString* key in fieldsandvalues.allKeys) {
[keyswithcolon addObject:[NSString stringWithFormat:#":%#", key]];
}
NSString* sql = [NSString stringWithFormat:#"INSERT INTO userphrase (%#) VALUES (%#)", [fieldsandvalues.allKeys componentsJoinedByString:#","], [keyswithcolon componentsJoinedByString:#","]];
// DLog(#"sql: %#", sql);
if (![self.db executeUpdate:sql withParameterDictionary:fieldsandvalues]) {
NSAssert(NO, #"Failed inserting userphrase into database! Last error: %# - %#", self.db.lastError, self.db.lastErrorMessage);
return nil;
}
Okay, so, I'm doing a simple lookup. I have an array of NSString objects and a string to search for in the array's elements.
It all seems to work up until I try to add a match to a new mutable array made to hold the search results. The stringHolder variable gets the string, and resultsCollectorArray even get the right number of new elements, but each element is empty and "out of range". Here's the method:
#implementation NSArray (checkForString)
-(NSMutableArray *) checkForString: (NSString *) matchSought
{
long unsigned numberofArrayElements;
long unsigned loop = 0;
NSRange searchResults;
NSMutableArray * resultCollectorArray = [[NSMutableSet alloc] init];
id stringHolder;
numberofArrayElements = [self count];
while (loop < numberofArrayElements) {
searchResults.length = 0;
searchResults = [[self objectAtIndex: loop] rangeOfString: matchSought options:NSCaseInsensitiveSearch];
if (searchResults.length > 0) {
stringHolder = [self objectAtIndex: loop];
[resultCollectorArray addObject: stringHolder];
}
loop++;
}
return [resultCollectorArray autorelease];
}
Once we get back to the main portion of the program, I get an unrecognized selector sent to the mutable array that was supposed to receive the result of the method. Here's the main section:
#import <Foundation/Foundation.h>
#import "LookupInArray.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *testString = [[NSString alloc] initWithString: #"ab"];
NSMutableString * resultString = [[NSString alloc] init];
NSArray * theArray = [[NSArray alloc] initWithObjects: ..., nil]; // Actual code has the objects
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSUInteger arrayCount = 0;
unsigned long loops = 0;
resultArray = [theArray checkForString: testString];
arrayCount = [resultArray count];
while (loops < arrayCount){
resultString = [resultArray objectAtIndex: loops]; // Here's where we get the unrecognized selector.
NSLog(#"%#", resultString);
loops++;
}
[pool drain]; // Also, I'll release the objects later. I just want to get what's above working first.
return 0;
}
I've searched the other answers (for hours now), but didn't seen anything that solved the issue.
Any and all help would be really appreciated.
And thanks beforehand.
NSMutableArray * resultCollectorArray = [[NSMutableSet alloc] init]; is so incorrect. You are creating a mutable set and assigning it to a mutable array.
You are getting unrecognized selector because objectAtIndex: is not a valid selector for NSMutableSet. Make that statement,
NSMutableArray * resultCollectorArray = [[NSMutableArray alloc] init];
A Better way
NSArray * filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains[cd] %#", searchString]];
You can directly filter the array using predicates. This way you do this in a single step. :)
I have gone through following question.
Convert NSString to NSDictionary
It is something different then my question.
My question is as follows.
NSString *x=#"<Category_Id>5</Category_Id><Category_Name>Motos</Category_Name><Category_Picture>http://192.168.32.20/idealer/admin/Picture/icon_bike2009819541578.png</Category_Picture>";
Now I want to convert this into a dictionary, something like this,
dictionary key = Category_Id | value = 5
dictionary key = Category_Name | value = ???
dictionary key = Category_Picture | value = ???
I don't know how to perform this.
Not the fastest implementation, but this would do the trick (and doesn’t require any third party libraries):
#interface NSDictionary (DictionaryFromXML)
+ (NSDictionary *)dictionaryFromXML:(NSString *)xml;
#end
#implementation NSDictionary (DictionaryFromXML)
+ (NSDictionary *)dictionaryFromXML:(NSString *)xml
{
// We need to wrap the input in a root element
NSString *x = [NSString stringWithFormat:#"<x>%#</x>", xml];
NSXMLDocument *doc = [[[NSXMLDocument alloc] initWithXMLString:x
options:0
error:NULL]
autorelease];
if (!doc)
return nil;
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (NSXMLElement *el in [[doc rootElement] children])
[dict setObject:[el stringValue] forKey:[el name]];
return dict;
}
#end
If it's XML then you can use an NSXMLParser. If it's not then you can write your own parser.
You could do it with a regular expression... Something like <([^>]+)>([^<]+)</\1> would grab the key into capture 1 and the value into capture 2. Iterate over the matches and build the dictionary.
This uses RegexKitLite:
NSString * x = #"<Category_Id>5</Category_Id><Category_Name>Motos</Category_Name><Category_Picture>http://192.168.32.20/idealer/admin/Picture/icon_bike2009819541578.png</Category_Picture>";
NSString * regex = #"<([^>]+)>([^<]+)</\\1>";
NSArray * cap = [x arrayOfCaptureComponentsMatchedByRegex:regex];
NSMutableDictionary * d = [NSMutableDictionary dictionary];
for (NSArray * captures in cap) {
if ([captures count] < 3) { continue; }
NSString * key = [captures objectAtIndex:1];
NSString * value = [captures objectAtIndex:2];
[d setObject:value forKey:key];
}
NSLog(#"%#", d);