I have an array NSMutableArray where I save an MKuserlocation type - locationArray.
anyway now I want to get the data from this array and save it to an array from type CLLocationCoordinate2D.
but since everything I save in locationArray is from id type how can I get the coordinates from this and save it to the second array?
CLLocationCoordinate2D* coordRec = malloc(pathLength * sizeof(CLLocationCoordinate2D));
for(id object in locationArray){
for (int i = 0; i < pathLength; i++)
?????
I dont know if this even possible!
Thanks
Why do you need a c-style array of CLLocationCoordinate2D objects?
Here you go:
NSArray* userLocations; // contains your MKUserLocation objects...
CLLocationCoordinate2D* coordinates = malloc( userLocations.count * sizeof( CLLocationCoordinate2D) );
for ( int i = 0 ; i < userLocations.count ; i++ )
{
coordinates[i] = [[[userLocations objectAtIndex: i] location] coordinate];
}
Refering to Apple docs
You should certainly use CLLocationCoordinate2DMake function
with data from MKUserLocation or directly extract infos from MKUserLocation:
object.location.coordinate // it's a CLLocationCoordinate2D from your 'object' example
or
CLLocationCoordinate2DMake(object.location.coordinate.latitude, object.location.coordinate.longitude)
Hope this help.
The typical solution is to create a NSObject subclass and define a single property, a CLLOcationCoordinate2D. Instantiate and add those objects to your array.
#interface Coordinate : NSObject
#property (nonatomic) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
#end
#implementation Coordinate
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate
{
self = [super init];
if (self) {
_coordinate = coordinate;
}
return self;
}
#end
And then, because your locationArray is an array of MKUserLocation (which, itself, conforms to MKAnnotation), you can do:
NSMutableArray *path;
path = [NSMutableArray array];
for (id<MKAnnotation> annotation in locationArray)
{
// determine latitude and longitude
[path addObject:[[Coordinate alloc] initWithCoordinate:annotation.coordinate]];
}
Or make an array of existing object type, such as CLLocation or MKPinAnnotation or whatever.
Or if this array is a path to be drawn on the map, you might want to avoid using your own array, and instead make a MKPolyline.
NSInteger pathLength = [locationArray count];
CLLocationCoordinate2D polylineCoordinates[pathLength]; // note, no malloc/free needed
for (NSInteger i = 0; i < pathLength; i++)
{
id<MKAnnotation> annotation = locationArray[i];
polylineCoordinates[i] = annotation.coordinate;
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:polylineCoordinates count:pathLength]
[self.mapView addOverlay:polyline];
It depends upon what the purpose of this is. But if you can use one of the previous constructs that avoids malloc and free, that's probably ideal. These techniques leverage Objective-C patterns which make it harder to leak, use an invalid pointer, etc.
Related
This is what I'm trying to do. Get 7 random, non-repeating numbers each time my viewDidLoad starts. I got it to create the randoms, but I've been trying to clear the NSMutableSet when it loads to get a fresh set and I'm having trouble. The NSLog is clearly showing nothing is in the NSMutableSet, but it always comes up with the same numbers in the same order?
// Create set
NSMutableSet *mySet = [NSMutableSet setWithCapacity:6];
// Clear set
NSMutableSet *mutableSet = [NSMutableSet setWithSet:mySet];
[mutableSet removeAllObjects];
mySet = mutableSet;
NSLog(#"mutableSet: %#", mutableSet); // Shows nothing
NSLog(#"mySet: %#", mySet); // Shows nothing
// Assign random numbers to the set
while([mySet count]<=6){
int Randnum = arc4random() % 7+1;
[mySet addObject:[NSNumber numberWithInt:Randnum]];
}
NSLog(#"mySet1: %#", mySet); // Always shows 5,1,6,2,7,3,4 ???
An NS(Mutable)Set is an unordered collection, it does not preserve the order of the elements as they were inserted. So your output only shows that the set contains the
numbers from 1 to 7.
You have different options to get your expected output.
Use an NSMutableOrderedSet instead.
Use a set to keep track of the numbers that already occurred, but store the
numbers also in an array:
NSMutableArray *numbers = [NSMutableArray array];
NSMutableSet *mySet = [NSMutableSet set];
while ([numbers count] < 6) {
NSNumber *randNum = #(arc4random_uniform(7) + 1);
if (![mySet containsObject:randNum]) {
[numbers addObject:randNum];
[mySet addObject:randNum];
}
}
NSLog(#"numbers: %#", numbers);
For a small set (like 7 numbers in your case), you could simply use an array only:
NSMutableArray *numbers = [NSMutableArray array];
while ([numbers count] < 6) {
NSNumber *randNum = #(arc4random_uniform(7) + 1);
if (![numbers containsObject:randNum]) {
[numbers addObject:randNum];
}
}
NSLog(#"numbers: %#", numbers);
I'm trying to create a basic quiz app where the questions will not repeat. I've looked at several examples and believe I should be storing the questions in an array and then removing one from the array each time its used. I've tried the following code.
- (void)viewDidLoad
{
//The array of questions
NSMutableArray *questionArray = [[NSMutableArray alloc] initWithObjects:
[NSMutableArray arrayWithObjects:#"First Question",#"Answer A", nil],
[NSMutableArray arrayWithObjects:#"Second Quesiton",#"AnswerA",#"AnswerB", nil],
nil];
//remove used question from array
for (int i = questionArray.count; i>=0; --i) {
_questions = [questionArray objectAtIndex:arc4random() % questionArray.count];
[questionArray exchangeObjectAtIndex:i withObjectAtIndex:_questions];
}
//use array object
self.lblQuestion.text = [_questions objectAtIndex:0];
[self.btnA setTitle:[_questions objectAtIndex:1] forState:UIControlStateNormal];
[super viewDidLoad];
}
I'm getting the following warning:
Incompatible pointer to integer conversion sending NSMutableArray*_strong to parameter of type 'NSUInteger'
I take it this means I shouldn't be using another array to store the random question as I can't use this to remove the question from the array. However I don't know how else to do this?
Am I completely misunderstanding how I should go about this?
Since your goal here is to get non-repeating questions...
I believe that instead of removing the question you have already used, you should SHUFFLE your array at the beginning and then loop through the array one index at a time using a simple counter.
I hope you can find this piece of code helpful -- give it a shot:
-(NSMutableArray *)shuffleArray:(NSMutableArray *)anArray
NSUInteger count = [anArray count];
for (NSUInteger i = 0; i < count; ++i)
{
int nElements = count - i;
int n = (arc4random() % nElements) + i;
[anArray exchangeObjectAtIndex:i withObjectAtIndex:n];
}
return anArray;
}
-(void)viewDidLoad
{
//The array of questions
NSMutableArray *questionArray = [[NSMutableArray alloc] initWithObjects:
[NSMutableArray arrayWithObjects:#"First Question",#"Answer A", nil],
[NSMutableArray arrayWithObjects:#"Second Quesiton",#"AnswerA",#"AnswerB", nil],
nil];
//Shuffle the question array -- Now all indexes are shuffled and Random
questionArray = [self shuffleArray:questionArray];
//Every time you want to move to the next question, all you have to do is connect a button to the nextIndex action and let it do all the work!
//Use the nextIndex method to initialise -- we call it manually the first time so things would get going and something is displayed -- you can remove the line below if you want it to initialise on first button click! Your call the shots sir!
[self nextIndex];
[super viewDidLoad];
}
//Edit -- This method shows how to change question array index using a method
int currentIndex = 0;
-(IBAction)nextIndex
{
if ( currentIndex == [questionArray count] )
{
currentIndex = 0; //Resets the var when getting to the final Index
//The above line will result in a question loop -- meaning if you arrive at the last question, the next question will be the first! Pacman mode!
//If you want to stop at the last question just change the above line to return; and you're all set!
}
//Set _questions object to the current Index Array element
_questions = [questionArray objectAtIndex:currentIndex];
//Increment currentIndex for next use
currentIndex++;
//use the array object to set your objects' values
self.lblQuestion.text = [_questions objectAtIndex:0];
[self.btnA setTitle:[_questions objectAtIndex:1] forState:UIControlStateNormal];
}
You will end up having totally different questions that are shuffled every time.
I hope you find this helpful.
I'm trying to compare two strings
NSString strOne = #"Cat, Dog, Cow";
NSString strTwo = #"Cow";
How do I determine if strOne contains strTwo
Try using rangeOfString:
NSRange result = [strOne rangeOfString:strTwo];
From the documentation:
Returns an NSRange structure giving the location and length in the receiver of the first occurrence of aString. Returns {NSNotFound, 0} if aString is not found or is empty (#"").
For anyone needing the code to check is a string exists within a string, here's my code thanks to fbrereto. This example checks to see if any string contained in an array of strings (stringArray) can be found within a string (myString):
int count = [stringArray count];
for (NSUInteger x = 0; x < count; ++x) {
NSRange range = [self.myString rangeOfString:[stringArray objectAtIndex:x]];
if (range.length > 0) {
// A match has been found
NSLog(#"string match: %#",[stringArray objectAtIndex:x]);
}
}
I believe this is the correct syntax for checking if the range exists (correcting response from Kendall):
range.location != NSNotFound
Gradually straying off topic, but I always explode my strings, which would mean just exploding it using your search string as a key and you can use the array count to see how many instances you have.
Just incase anyone is coming from a code language that uses "explode" to blow a string up into an array like me, I found writing my own explode function tremendously helpful, those not using "explode" are missing out:
- (NSMutableArray *) explodeString : (NSString *)myString key:(NSString*) myKey
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSRange nextBreak = [myString rangeOfString:myKey];
while(nextBreak.location != NSNotFound)
{
[myArray addObject: [myString substringToIndex:nextBreak.location]];
myString = [myString substringFromIndex:nextBreak.location + nextBreak.length];
nextBreak = [myString rangeOfString:myKey];
}
if(myString.length > 0)
[myArray addObject:myString];
return myArray;
}
works like this:
[self explodeString: #"John Smith|Age: 37|Account Balance: $75.00" key:#"|"];
which will return this array:
[#"John Smith", #"Age: 37", #"Account Balance: $75.00"];
This lets you quickly pull out a specific value in a tight space, Like if you have a client and you want to know how much money he has:
[[self explodeString: clientData key: pipe] objectAtIndex: 1];
or if you wanted specifically the dollar amount as a float:
[[[self explodeString: [[self explodeString: clientData key: pipe] objectAtIndex: 1] key: #": "] objectAtIndex: 2] floatValue];
anyway I find arrays way easier to work with and more flexible, so this is very helpful to me. Additionally with a little effort you could make an "explodable string" data type for your private library that lets you treat it like a string or return an index value based on the key
ExplodableString *myExplodableString;
myExplodableString.string = #"This is an explodable|string";
NSString *secondValue = [myExplodableString useKey: #"|" toGetValue: index];
I'm a bit of a NSSortDescriptor n00b. I think, though, it is the right tool for what I need to do:
I have an NSArray consisting of objects with keys, say, "name" and "time". Instead of verbalizing it, here's an example:
input:
name: time
B: 4
C: 8
B: 5
C: 4
A: 3
C: 2
A: 1
A: 7
B: 6
desired output:
name: time
A: 1 <---
A: 3
A: 7
C: 2 <---
C: 4
C: 8
B: 4 <---
B: 5
B: 6
So the values are sorted by "time" and grouped by "name". A comes first because he had the smallest time value, and all values for A come after one another. Then comes C, he had the second smallest time value out of all his values. I have indicated the values that determine how the names are sorted; within each name group, sorting is by time.
How do I get from input to output NSArray in the most efficient way? (cpu- and memory-wise, not necessarily code-wise.) How would I construct the NSSortDescriptors for this, or use any other method? I don't want to roll my own unless it's the most efficient way.
My solution is:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
You can try it
The sortedArrayUsingDescriptors: NSArray method does most of what you need:
The first descriptor specifies the primary key path to be used in sorting the receiver’s contents. Any subsequent descriptors are used to further refine sorting of objects with duplicate values. See NSSortDescriptor for additional information.
Some filtering with NSPredicate is required too:
NSSortDescriptor *timeSD = [NSSortDescriptor sortDescriptorWithKey: #"time" ascending: YES];
NSMutableArray *sortedByTime = [UnsortedArray sortedArrayUsingDescriptors: timeSD];
NSMutableArray *sortedArray = [NSMutableArray arrayWithCapacity:[sortedByTime count]];
while([sortedByTime count])
{
id groupLead = [sortedByTime objectAtIndex:0];
NSPredicate *groupPredicate = [NSPredicate predicateWithFormat:#"name = %#", [groupLead name]];
NSArray *group = [sortedByTime filteredArrayUsingPredicate: groupPredicate];
[sortedArray addObjectsFromArray:group];
[sortedByTime removeObjectsInArray:group];
}
I have no idea if this is the most efficient method, but until you have reason to believe that it is causing problems there's no need to worry the performance implications. It's premature optimisation. I wouldn't have any concerns about the performance of this method. You've got to trust the framework otherwise you'll end up rewriting it (thus undermine the point of the framework) due to an unfounded paranoia.
I would create a new class called ItemGroup, and then add an extra ivar called group to your item class:
#interface ItemGroup : NSObject
{
NSNumber * time;
}
#property (nonatomic, copy) time;
#end
#interface ItemClass : NSobject
{
NSString * name;
NSNumber * time;
ItemGroup * group;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSNumber * time;
#property (nonatomic, assign) ItemClass * group; // note: must be assign
#end
Then, you could do the following:
NSMutableDictionary * groups = [NSMutableDictionary dictionaryWithCapacity:0];
for (ItemClass * item in sourceData)
{
ItemGroup * group = [groups objectForKey:item.name];
if (group == nil)
{
group = [[ItemGroup alloc] init];
[groups setObject:group forKey:item.name];
[group release];
group.time = item.time;
}
else if (item.time < group.time)
{
group.time = item.time;
}
item.group = group;
}
This code loops through the unsorted array, keeping track of the minimum time for each group, and also setting the group for each item. With that complete, you simply sort on group.time and time:
NSSortDescriptor * groupSorter;
groupSort = [NSSortDescriptor sortDescriptorWithKey:#"group.time" ascending:YES];
NSSortDescriptor * timeSorter;
timeSort = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObjects:groupSort, timeSort, nil];
NSArray * sorted = [sourceData sortedArrayUsingDescriptors:sortDescriptors];
And that should do the trick!
UPDATE: Note that you could get much better performance if you were able to assign the groups straight out of the gate. Something like this:
#interface ItemGroup : NSObject
{
NSString * name;
NSNumber * time;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSSNumber * time;
#end
#interface ItemClass : NSObject
{
ItemGroup * group;
NSNumber * time;
}
#property (nonatomic, retain) ItemGroup * group;
#property (nonatomic, copy) NSNumber * time;
#end
Now, if you maintain a list of groups somewhere (they could even go in an array somewhere, if need be):
ItemGroup * group_A = [[ItemGroup alloc] init];
group_A.name = #"A";
ItemGroup * group_B = [[ItemGroup alloc] init];
group_B.name = #"B";
...
And instead of setting the names of your data items, you set their group:
someItem.group = group_A;
someItem.time = GetSomeRandomTimeValue();
[sourceData addObject:someItem];
....
This would greatly simplify the loop used to set group times:
for (ItemClass * item in sourceData)
{
if (item.time < group.time) { group.time = item.time; }
}
And, if you really wanted to be blazing fast about it, you could even modify the property setter for your time property to set the group times on the fly:
#implementation ItemClass
- (void)setTime:(NSNumber *)newTime
{
if (newTime < group.time) { group.time = newTime; }
time = [newTime copy];
}
#end
Note that you would have to be sure that group had been set before you set the time. With this in place, you wouldn't need that sorting loop at all. The sortDescriptors would be enough.
I went through to make a little code (didn't try running it or really go over it so there might be a couple of mistakes, but it has the general idea) to do what you're looking for. Performance wise, it probably won't be the best if you start running into huge amounts of data. I'm sure there's a better way to do this, but I felt like doing it the most basic way as a "temporary fix" answer.
NSMutableArray *copiedarray = [YourFirstArray mutableCopy];
NSMutableArray *sortedarray = [[NSMutableArray alloc] init];
NSMutableArray *tempgroup = nil;
NSSortDescriptor * groupSorter = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSInteger i;
NSInteger savedlowest = -1;
NSString *savedname = #"";
while ([copiedarray count] > 0) {
///reset lowest time and group
savedlowest = -1;
savedname = #"";
///grab the lowest time and group name
for (ii = 0;ii < [copiedarray count]; ii++) {
if (savedlowest==-1 || ((YourClass *)([copiedarray objectAtIndex:ii])).time<savedlowest)) {
savedname = ((YourClass *)([copiedarray objectAtIndex:ii])).name;
savedlowest = ((YourClass *)([copiedarray objectAtIndex:ii])).time;
}
}
//we have the lowest time and the type so we grab all those items from the group
tempgroup = [[NSMutableArray alloc] init];
for (ii = [copiedarray count]-1;ii > -1; ii--) {
if ([((YourClass *)([copiedarray objectAtIndex:ii])).name isEqualToString:savedname]) {
///the item matches the saved group so we'll add it to our temporary array
[tempgroup addObject:[copiedarray objectAtIndex:ii]];
///remove it from the main copied array for "better performance"
[copiedarray removeObjectAtIndex:ii];
}
}
[tempgroup sortUsingDescriptors:[NSArray arrayWithObject:groupSorter]];
[sortedarray addObjectsFromArray:tempgroup];
[tempgroup release];
tempgroup = nil;
}
In the end you'll end up with what you're looking for in sortedarray.
You can use NSSortDescriptor. These descriptors are very useful as they let you do multiple key sort as well single key sorting. The case sensitivity and insensitivity is also easily achievable. I found a detailed example HERE
If you have to do more complicated sorting the just "ascending" can take care of (say sort NSString as if they were floats), you might want to do something like this:
NSDictionary *d = [self dictionaryFromURL:[NSURL URLWithString:urlStringValue]];
NSSortDescriptor *distanceSort = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES comparator:^(id left, id right) {
float v1 = [left floatValue];
float v2 = [right floatValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:#"company_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:distanceSort, nameSort, nil];
[distanceSort release];
NSArray *sortedObjects = [[d allValues] sortedArrayUsingDescriptors:sortDescriptors];
ILog();
return sortedObjects;
I have an method that reads an xml file and stores the xml nodes at a certain XPath-path in an NSArray called *nodes. What I want to do is take each one of the items in the array and add it to a core data entity called Category with the attribute of "name".
I have tried a number of different ways of creating the entity but I'm not sure about the correct way to do this effectively. This is the code used to create the NSArray, any ideas on how to implement this? (ignore the NSError, I will fix this in the final version)
- (IBAction)readCategories:(id)sender
{
NSString *xmlString = [resultView string];
NSData *xmlData = [xmlString dataUsingEncoding: NSASCIIStringEncoding];
NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithData:xmlData options:nil error:nil];
//XPath
NSError *err=nil;
NSArray *nodes = [xmlDoc nodesForXPath:#"//member[name='description']/value/string" error:&err];
}
EDIT - My loop code
NSArray *nodes = [xmlDoc nodesForXPath:#"//member[name='description']/value/string" error:&err];
int arrayCount = [nodes count];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLElement *categoryEl;
NSString *new = [catArrayController newObject];
int i;
for (i = 0; i < arrayCount; i++)
{
[categoryEl = [nodes objectAtIndex:i]];
[new setValue:[categoryEl stringValue] forKey:#"name"];
[catArrayController addObject:new];
}
[pool release];
Here's how I'd write it:
for (NSXMLElement *categoryElement in nodes) {
NSManagedObject *newObject = [catArrayController newObject];
[newObject setValue:[categoryElement stringValue] forKey:#"name"];
[catArrayController addObject:newObject];
[newObject release];
}
First, I'm using the Objective-C 2.0 for-each syntax. This is simpler than using index variables. I eliminated i and arrayCount.
Next, I took out your NSAutoreleasePool. None of the objects in the loop are autoreleased, so it had no effect. (The newObject method returns a retained object which is, by convention, what methods with the word new in their name do) This is also why I release newObject after adding it to the array controller. Since I'm not going to be using it any more in this method, I need to release it.
Also, you had defined new (which I renamed newObject) as an NSString. Core Data objects are always either an instance of NSManagedObject or a subclass of NSManagedObject.
Your line [categoryEl = [nodes objectAtIndex:i]] won't compile. That's because the bracket syntax is used to send a message to an object. This is an assignment statement, so the bracket syntax is not needed here. (This line is also not necessary any more because of I've changed the loop to use the for-each syntax) But, for future reference, categoryEl = [nodes objectAtIndex:i]; would have worked.
What part are you having trouble with? There shouldn't be much more to it than looping through the array, creating a new managed object for each entry, and setting the correct attributes. You can create the managed object with NSEntityDescription's -insertNewObjectForEntityForName:inManagedObjectContext: method.