I have a NSMutableArray, I want to insert data inside it, the problem is first I want to check if the index where I'm inserting the data exists or not. How to do that?
I try something like that but nothing is working:
if ([[eventArray objectAtIndex:j] count] == 0)
or
if (![eventArray objectAtIndex:j])
if (j < [eventArray count])
{
//Insert
}
NSArray and NSMutableArray are not sparse arrays. Thus, there is no concept of "exists at index", only "does the array have N elements or more".
For NSMutableArray, the grand sum total of mutable operations are:
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
All other mutability methods can be expressed in terms of the above and -- more specifically to your question -- removing an object does not create a hole (nor can you create an array with N "holes" to be filled later).
I've given a brief implementation of a sparse array in this question: Sparse Array answer
Related
I am not sure about how NSSet's anyObject work. What does it mean that "The object returned is chosen at the set’s convenience" (from the NSSet class reference) ?
Further, how can I best extract objects randomly from a NSSet? I was thinking about getting allObjects in an array and then myArray[arc4random_uniform(x)] where x is the number of objects in the array.
Quote from NSSet Class Reference:
The object returned is chosen at the set’s convenience—the selection is not guaranteed to be random.
For "randomness", convert the NSSet to an NSArray using [theSet allObjects].
Next, pick any object randomly using arc4random_uniform().
Usually, NSSet instances are created with a CFHash backing, so they almost always return the first object in that hash, as it is the fastest to look up. The reason it says
The object returned is chosen at the set’s convenience—the selection is not guaranteed to be random.
Is because you don't always know it will have a backing array. For all you know, the NSSet instance you have has a NSDictionary backing it, or some other similar data structure.
So, in conclusion, if you need a random object from a NSSet, don't use -anyObject, instead use allObjects: and then shuffle that array.
The documentation reads that anyObject returns
One of the objects in the set, or nil if the set contains no objects.
The object returned is chosen at the set’s convenience—the selection
is not guaranteed to be random.
Most likely there is some deterministic algorithm at work.
The most reliable thing to do would be, as you suggest, to create an NSArray using the NSSet method allObjects, and then choose a random element from that with arc4random() % N where N is the count of the NSArray.
I use arc4random() and two mutable arrays to get a random and unique set of objects:
NSMutableArray *selectionPool = ...;
int numberOfObjectsToSelect = x;
NSMutableArray *selectedObjects = [[NSMutableArray alloc] initWithCapacity:numberOfObjectsToSelect];
int modulus = selectionPool.count - 1;
for (int i = 0; i < numberOfObjectsToSelect; i++) {
int j = arc4random() % (modulus--);
[selectedObjects addObject:[selectionPool objectAtIndex:j]];
[selectionPool removeObjectAtIndex:j];
}
I'm not sure how efficient it would be for large collections, but it's worked for me with collections that number in the low 100s of objects.
Which is quicker and less expensive for checking if an object already exists within a list. By using the NSArray contains object or by checking if a key already exists for an NSDictionary?
Also does the NSArray containObject selecter iterate through the whole array elements? Also what about checking if a key already exists within a dictionary? Does that require iterating through all the keys.
Finally, what is the best and quickest way to check if an object already exists within a large list of objects (of the same class).
Thanks in advance
According to the document of Collection Classes the NSDictionary is based on HashTables. Which means if you are searching for a key in a dictionary, the time required is mush less than iterating through an array.
So, searching for a key should be o(1+numberofcollisions). where iterating through an array is o(n). You can quick sort array then binary search it which will make the cost a lot less. However for your buck, NSDictionary (hash table) are very cheap for searching.
From Apple docs
Internally, a dictionary uses a hash table to organize its storage and to provide rapid access to a value given the corresponding key. However, the methods defined for dictionaries insulate you from the complexities of working with hash tables, hashing functions, or the hashed value of keys. The methods take keys directly, not in their hashed form.
How many values are you talking about? The difference in speed may be irrelevant, thus making the choice be the one that makes the most sense in the code. In fact, that should probably be the first priority, unless and until you know that there is a speed problem.
Short version: Use NSDictionary unless you have a specific need not to.
I would say that the fastest way would be to sort your array when you insert an object:
NSMutableArray *myArray;
[myArray addObject:someCustomObject];
[myArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
// custom compare code here
}];
While this takes performance out of inserting an object, it would greatly increase your lookup times.
To do a binary search on a NSArray:
BOOL binarySearchContains(NSArray *sortedArray, id object, NSComparator comparisonBlock)
{
// simple recursive helper function
__block BOOL (^_binaryRecurse)(NSArray *, id, int lo, int hi) = ^BOOL(NSArray *array, id object, int lo, int hi)
{
int middleIndex = ((hi - lo) / 2) + lo;
if (hi == lo || middleIndex < 0 || middleIndex >= [array count])
return NO;
int compareResult = (comparisonBlock(object, [array objectAtIndex:middleIndex]));
if (compareResult < 0)
return _binaryRecurse(array, object, lo, middleIndex - 1);
if (compareResult > 0)
return _binaryRecurse(array, object, middleIndex + 1, hi);
return YES;
};
return _binaryRecurse(sortedArray, object, 0, [sortedArray count]);
}
In my tests, the bsearch is approximately 15 times faster than -containsObject:.
I'm adding an object to a NSMutableArray like this:
[array addObject:myObject];
I now want to send a reference to my delegates of the Array Index where this object was added.
Is there an easy way to find out the index where my object was added in the array so that later I can call
[array objectAtIndex:index]
to get a reference back for it?
Thanks!
Rather than passing the index of an object (which could be incorrect) to your delegate, pass a reference to the object itself. If the delegate needs the index of the object in the array, it can figure it out itself using -indexOfObject: as Antonio MG describes. The index of any given object in a mutable array can change as objects are added, inserted, and deleted. Counting on indices to remain valid over any period of time is like leaving a jelly sandwich on the counter -- it's sure to attract bugs.
You need to serialize access to a mutable array and -addObject: always adds the object to the end of the array. Given those two assertions, you know the index of the next added object will always be the current length of the array. So the following will hold true:
NSUInteger nextIndex = [array count];
[array addObject:myObject];
// you can now tell your delegates that nextIndex is the index of myObject
Use this method for that:
index = [animalOptions indexOfObject:myObject];
The latest added object should be at [array count] - 1 index. You can always rely on "count - 1" scheme to determine the last index.
If you call addObject you always add the object at the end (so count - 1).
You can use "insertObject:atIndex:" to specify an index.
For your question: indexOfObject:
Direct after adding the object's index is array.count -1 .
Am confused with the use of this method and the documentation that lists it as a (void) method.
"on return the index path's indexes"
where does it return anything too?
Should it not be:
- (NSIndexPath *)getIndexes:(NSUInteger *)indexes
getIndexes:
Provides a reference to the index path’s indexes.
- (void)getIndexes:(NSUInteger *)indexes
Parameters
indexes
Pointer to an unsigned integer array. On return, the index path’s indexes.
Availability
Available in iOS 2.0 and later.
Declared In
NSIndexPath.h
You have to allocate the NSUInteger array of size [indexPath length] and pass it as argument. The return value will be written there. You have to release that array yourself or do nothing it was created on stack like this:
NSUInteger array[[indexPath length]];
[indexPath getIndexes: array];
Maybe the next sentence explains the reason
It is the developer’s responsibility to allocate the memory for the C array.
It's actually a pointer to a C array that will be filled for you with the indexes, so there's no reason to additionally return it from the function - you already know its address.
You can use the function as follows
NSUInteger indexCount = [indices count];
NSUInteger buffer[indexCount];
[indices getIndexes:buffer maxCount:indexCount inIndexRange:nil];
You send that message to an instance of NSIndexPath, so getting one back wouldn't help. The -getIndexes: method fills the array 'indexes' with the indexes from the index path. So you'd do something like:
NSUInteger *indexes = calloc([indexPath length], sizeof(NSUInteger));
[indexPath getIndexes:indexes];
After that, indexes will be filled with the index values that are in indexPath.
Max, Thomas and Caleb, I have tried all three ways and cannot get anything to work, so maybe fired off the accepted solution too quick.....but probably more likely I just don't get it? I can't get the right size of the array in order to loop through it to access the required rows in my table. I would have though that calloc([indexPath length], sizeof(NSUInteger)) would for a group with 5 rows return an array with 5 rows with each row holding NSUIntegr.....or am of so far off beam it embarrassing?
My aim is to produce an array, which I can use to add section headers for a UITableView. I think the easiest way to do this, is to produce a sections array.
I want to create section headers for dates, where I'll have several or no rows for each.
So in my populate data array function, I want to populate a display array. So record 1, look for the first date in my display array, create a new array item if it doesn't exist, if it does exist add 1 to the count.
So I should end up with something like this.
arrDisplay(0).description = 1/June/2001; arrDisplay(0).value = 3;
arrDisplay(1).description = 2/June/2001; arrDisplay(1).value = 0;
arrDisplay(2).description = 3/June/2001; arrDisplay(2).value = 1;
arrDisplay(3).description = 5/June/2001; arrDisplay(3).value = 6;
My question is how do I create and use such an array with values, where I can add new elements of add to the count of existing elements and search for existing elements ?
I think, if i understand you, an NSMutableDictionary would work. (as NR4TR said) but, i think the object would be the description and the key would be the count. you could check for the key and get the count in the same gesture. if the return value of objectForKey is nil, it doesn't exist.
NSMutableDictionary *tableDictionary = [[NSMutableDictionary alloc] init];
NSString *displayKey = #"1/June/2001";
NSNumber *displayCount = [tableDictionary objectForKey:displayKey];
if (displayCount != nil) {
NSNumber *incrementedCount = [[NSNumber alloc] initWithInteger:[displayCount integerValue] + 1];
[tableDictionary removeObjectForKey:displayKey];
[tableDictionary setValue:incrementedCount
forKey:displayKey];
[incrementedCount release];
}
else {
NSNumber *initialCount = [[NSNumber alloc] initWithInteger:1];
[tableDictionary setValue:initialCount
forKey:displayKey];
[initialCount release];
}
EDIT: Hopefully this isn't pedantic, but I think a couple pointers will help.
Dictionaries, Sets, and Arrays all hold objects for retrieval. The manner of holding and retrieval desired drives the decision. I think of it based on the question 'what is the nature of the information that I have when I need an object being held?'
NSDictionary and NSMutableDictionary
Hold n objects per key. (I think...I haven't had to test a limit, but i know you can get an NSSet back as a value.)
KEY is more important than INDEX. I don't think of dictionaries as ordered. they know something and you need to ask the correct question.
NSArray and NSMutableArray
hold n objects in order.
INDEX is most important bit of information. (you can ask for the index of an object but, even here, the index is the important part)
you will typically drive table views with an array because the ordered nature of the array fits.
NSSet, NSMutableSet, and NSCountedSet
A collection of objects without order.
You can change any of these into the other with something like [nsset setFromArray:myArray];
and all of these things can hold the other as objects. I think an array as your top level is the correct thinking, but beyond that, it becomes an issue of implementation
Try array of dictionaries. Each dictionary contains two objects - section title and array of section rows.
If you want to have a description AND a rowcount then you can either create a class with those two properties and generate an NSArray of objects with that class or instead of all that you can just use an NSDictionary to store key/value lookups.
I think NSCountedSet is closest to what you want. It doesn't have an intrinsic order, but you can get an array out of it by providing a sort order.