Filling NSMutableArray for later use in obj-c - objective-c

How do you fill a NSMutableArray with a set capacity for later use?
Basically I want to set up a NSMutableArray to act as a map for my game objects, so I have this line...
gameObjects = [[NSMutableArray alloc] initWithCapacity:mapWidth*mapHeight];
Which I had hoped would create and fill my MutableArray so I can get then access it with this kind of index...
int ii = (cellY*mapWidth)+cellX;
NSDictionary *currentObject = [gameObjects objectAtIndex:ii];
But I just learned initWithCapacity doesn't fill the array, so should I create blank objects to fill it with, or is there a Null that I can fill it with? Also would I do that with 2 for loops or is there an instruction something like "initWith:myObject" ?
I want to be able to check at a certain index within the array to see if there's an object there or not, so I need to be able to acces that index point, and I can only do that if there's something there or I get an out of bounds error.
I'll be using this NSMutableArray pretty much as a grid of objects, it's a 1 dimensional array organised as a 2 dimensional array, so I need to be able to fill it with mapWidth*mapHeight of something, and then calculate the index and do a check on that index within the array.
I've looked on here and googled but couldn't find anything like what I'm asking.
Thanks for any advice.

I think what you are looking for is [NSNull null]. It is exactly what you want- a placeholder value.
You can find more information on the topic in this question.

initWithCapacity is just a performance optimization -- it has no effect on the array behavior, it just keeps the code "under the covers" from having to repeatedly enlarge the internal array as you add more entries.
So if you want a "pre-allocated" array, you'd need to fill it with NSNull objects or some such. You can then use isKindOfClass to tell if the object is the right type, or simply == compare the entry to [NSNull null]. (Since there's only ever one NSNull object it always has the same address).
(Or you could use a C-style array of pointers with nil values for empty slots.)
But you might be better off using an NSMutableDictionary instead -- no need to pre-fill, and if the element isn't there you get a nil pointer back. For keys use a NSNumber object that corresponds to what would have been your array index.

initWithCapacity only hints to NSMutableArray that it should support this many objects. It won't actually have any objects in it until you add them. Besides, every entry in the array is a pointer to an object, not a struct like you'd normally have in a standard c array.
You need to change how you're thinking about the problem. If you don't add an object to the array, it's not in there. So either you pre-fill the array with "empty" objects as you've said, which is weird. Or you can add the objects as you need them.

Related

Changing NSMutableArray size in objective-c

I was looking around and couldn't find anything, and I'm starting to think it's not possible with objective-c.
I have a NSMutableArray *myMutableArray and the size varies depending on what csv file is loaded. Since I do not set a size of myMutableArray I can't do:
if (c == 5){
myMutableArray[q] = [[NSNumber numberWithFloat:myOtherArray] stringValue];
q = q + 1;
c = 0;
}
Else {
c = c + 1;
}
Since myMutableArray is technically of size nil I guess I can't add objects to it.
In cases, q can be between 1500 and 2500.
My question is, how do I make `myMutableArray' change size on every loop.
If this isn't possible, I guess I will have to make myMutableArray very large - but I need the values in myMutableArray for a graph. If I do myMutableArray = [[NSMutableArray alloc] initWithCapacity:5000]; and don't use the 5000 memory locations, will these locations be nil, or 0? (sorry if the technical words are wrong, by memory locations I mean the memory given to myMutableArray)
Thank you, if there is anything else I can add to this please feel free to let me know.
EDIT: What I'm trying to achieve is adding data to the array, and with that data create a graph
You can't have a sporadically populated array. Creating an array with capacity 5000 is just a hint as to how much data you might be going to store into the array. Consider using an array of dictionaries where the dictionary contains the q value (presumably one coordinate) and the associated value. You can then sort the array based on the q values if you need to (for plotting). Then you can just add the dictionaries to the array as usual (addObject:).
The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray.
If you
arrayWithCapacity:
Creates and returns an NSMutableArray object with enough allocated memory to initially hold a given number of objects.
Mutable arrays expand as needed. When declaring them, we can init them like this:
+ (instancetype)arrayWithCapacity:(NSUInteger)numItems
Here numItems simply establishes the object’s initial capacity.
Later to add more data, i.e. to expand mutable array, use this
addObject:
What it does is, it inserts a given object at the end of the mutable array.
- (void)addObject:(id)anObject
It's important to note that:
The object to add to the end of the array's content. This value must not be nil. It raises an NSInvalidArgumentException if anObject is nil.

ObjC: Best use an NSArray or NSDictionnary for this (zBuffer)?

Say I have a collection of "node" instances. An integer property call zIndex will be used to group them.
What are the pros/cons for storing them in :
1) An array of arrays
2) A dictionary of arrays
In pseudo code, I would describe the expected result like this:
zBuffer[100] = [node1, node 2];
zBuffer[105] = [playerNode, collectable1];
zBuffer[110] = [foreground1, foreground2];
And I'm wondering about what zBuffers should be; Must NSArrays only be used for sequential read/write? Like not using non-continuous indexes?
I tried with an NSMutableArray:
[zBuffer objectAtIndex:zOrder]
But it fails if the array contains no data for that index (like out-of-bound exception).
Thanks for your advices!
J
As far as I can see, one of your requirements is that the indexes you use to access zBuffer be not contiguous (100, 105, 100). In this case, I would not use an array for that, since the indexes you can use with an array must be less than the count of elements of the array (if you have 3 elements, then indexes range from 0 to 2).
Instead I would use NSMutableDictionary, where you can use the zIndex key as a "name" for groups of objects you are looking for.
This suggestion does not take into account any other requirements that you might have, especially concerning complexity and the kind of operations you are going to carry through on your collection of nodes (beyond accessing them through zIndex).
You could actually provide both. It looks like what you want to have is a sparse array: so you look up objects by index, but it's permissible for there not to be an object at a certain index. So you could make that.
I'd do that by creating an NSMutableArray subclass that implements the primitive methods documented. Internally, your subclass would use an NSMutableDictionary for storage, with numbers (the "filled" indices) as keys. -objectAtIndex: returns either the object with that number as its key or nil if the array is empty at that point.
There are some ambiguities in this use of the array contract that it's up to you to decide how to address:
does count return 1+(highest index in use), or the number of objects in the array?
the enumerator and fast enumeration patterns never expect to see nil, so you need to come up with an enumerator that always returns an object (but lets me see what index it's at) if you want users of your class to enumerator over the array.
you won't be able to initialise it with the +arrayWithObjects: (id) firstObject,... pattern of initialisers because they use nil as a sentinel.

How do I add data into NSMutableArray by manually assigning index?

Is there any way to manually control which index we want to add our data to with an NSMutableArray? In PHP, for example, we can do:
$foo[2] = "fafa";
$foo[8] = "haha";
Can the manual assigning of indexing be done on NSMutableArray?
NSMutableArrays are not sparse -- they cannot have gaps where no objects are stored. It is possible to insert an object at any index from 0 to length - 1, using insertObject:atIndex: (as Edu mentioned), but other than that you can only append objects (using addObject:).
If you need to associate objects with arbitrary integers, you can use an NSMapTable -- the functional interface allows integer keys, or simply use an NSMutableDictionary, with NSNumber keys (as drewag suggested).
See also: How to do sparse array in Cocoa
Just because it's not entirely clear to me that TeamStar wants a sparse array, I'll just mention that you can assign objects to specific indexes within an NSMutableArray – so long as the array is already long enough – using -replaceObjectAtIndex:withObject: and the other -replace… methods.
No that is not possible. I would suggest using an NSMutableDictionary instead. You can use NSNumbers as indexes.
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
Important Raises an NSRangeException if index is greater than the number of elements in the array.

taking out objects from NSMutableArray

I have an array which contains objects some may be same and some are different.
How can I take each same objects and different objects separately ?
Below is the array
NSMutableArray *items = [[NSMutableArray alloc]
initWithArray:[NSArray arrayWithObjects:#"rat", #"rat", #"cat",#"Lion", #"cat", #"dog", #"dog", nil]];
I want to have four arrays which will contains these items :
First array with two rats
2nd array with two cats
3rd array with one lion
4th array with two dogs
What could be the best way to take the objects out ? Identical object should be placed in same array.
Here's a general answer:
Put the array into an NSCountedSet - that will store each object and a count of the number of times it has been added.
Then - for each object in this counted set create an array with that object repeated according to the count of each object.
It will work in your case, because you are using static strings, which are going to be the same if they are the same string. This will take more work if you are using custom objects.
But the real question we have to ask is why you need to create these repetitive structures. If we could know what you are doing with it, we could give you better advice about how to go about it. For example, if you just need to keep a running count of the number of each type of object you have, you could just use the NSCountedSet directly (it descends from NSMutableSet, so it is already mutable) and not bother with creating the arrays.

Merge NSMutableArray with a NSArray, filtering the duplicates

I have two arrays, an NSMutableArray and an NSArray. The NSMutableArray is the "store", it stores results from a source of NSArrays. Every 5 minute, a new NSArray comes in and the data needs to be filtered and sorted.
Sorting by date is pretty easy, so I managed to get the NSArray sorted by NSDate. Sorting the other array is not necessary, as it would only cause confusion with the user.
What I want to do: the NSArray has a lot of different objects that all respond to -[object name], returning an NSString. The NSArray needs to be merged into the NSMutableArray, only adding new objects.
The merging itself is no problem, but performance is. The NSMutableArray can contain up to 3000 items, and the NSArray can contain up to 250 items, although usually only 5 or 6 of these have to be merged into the NSMutableArray.
So, my question is: how do you merge two arrays in Objective-C, filtering the duplicates, without iterating (250*3000) times?
Tom
Edited to clarify something
The "duplicate" objects are objects that are duplicate to the user but not to the code. They have the same name, but not the same address.
More clarification: #"value" != #"value" // true
Is name a property of the objects being stored in the arrays? If so, you could use a fairly simple NSPredicate to filter the immutable array before adding the results to the mutable one. Here's an example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NONE name == %#.name", mutableArray];
resultsArray = [immutableArray filteredArrayUsingPredicate:predicate];
[mutableArray addObjectsFromArray:immutableArray];
How about this:
[mutable removeObjectsInArray:newArray];
[mutable addObjectsFromArray:newArray];
It isn't the fattest, but is easy to implement :)
Edited to remove some stupidity (left plenty, though)
A couple of options:
Remove all matching objects from the NSMutableArray using removeObjectIdenticalTo. This requires iterating through the smaller array, but as you note they're commonly small. Then,
Add all of the items from the new array using addObjectsFromArray
Or... well, it actually might be faster to instead:
Iterate through the new array looking for matches with indexOfObjectIdenticalTo, using addObject to add in non-matching objects.
Costly either way, but doable.
I would probably start by creating a new mutable array which contains the contents of your NSMutableArray and NSArray. Then, sort the new array based on the name property and then run through the array once, only pulling out the unique items.
Can you use NSSet and NSMutableSet instead? That could help deal with the duplicates issue.
Edit:
Based on your comments, you could use an NSSet to check for object membership quickly, in addition to your array. It'd require a bit more memory, but if you don't mind that, it could allow you to check really fast. You'd have your NSMutableArray backing store, and then an NSSet to keep track of object membership. You'd maintain the invariant that the NSMutableArray does not contain duplicates. You could use code like this:
// Assume that arrayStore is an NSMutableArray * instance variable
// Also, storeSet is an NSMutableSet * ivar
- (void)addObjectsFromArray:(NSArray *)data
{
for (id item in data) {
if (![storeSet member:item]) {
// Will have to keep arrayStore sorted somehow
[arrayStore addObject:item];
[storeSet addObject:item];
}
}
}
You only have to iterate through the NSArray. I'm not sure how NSSet is implemented off the top of my head, but checking for membership won't be an O(n) operation like it is for an unsorted array.
It's not the most efficient method, but it works well with what you already have in place, with minor modifications.
There are likely many ways to dramatically improve performance, but to be able to suggest any, we really need to know more about what the objects in the arrays "are": what do they represent? How are they being used? (For example, are the items in the store array being displayed in a table view?)
NSMutableDictionary, NSMutableSet, etc. could be combined with NSMutableArray to organize and implement the model in an efficient manner.
For example, let's say we know the object represents a person: MDPerson. A person has a gender, a date of birth, a name, a unique id, and a set of attributes that can change. Given this higher level understanding of what the object represents, we know that 2 people are equal only if their unique ids are the same (in other words, 2 different people can have the same name, gender, and date of birth). Let's say that your main NSMutableArray is made up of a list of 3000 people. The incoming array is made up of 500 people which are already in the main NSMutableArray. A few of these 500 people instances might have "updated" attributes, which means that their instance in the main array needs to be updated with that info.
Given that understanding, it's clear that the main list should be implemented as an NSMutableDictionary rather than an NSMutableArray. In the dictionary, the person's unique id would be the key, and their person instance would be the value for the key. You could then loop through the incoming array of 500 persons only once:
// main dictionary is called personIDsAndPersons
for (MDPerson *person in incomingPersons) {
MDPerson *existingPerson = [personIDsAndPersons objectForKey:[person uniqueID]];
// if nil, the person doesn't exist
if (existingPerson) {
// update the existing person's attributes
[existingPerson setUniqueAttributes:[person uniqueAttributes]];
}
}
Again, without knowing more of the details or having a higher level understanding of what the objects are, we're really just shooting in the dark.
You mention that 2 items are only the same if they have the same name. So, does that mean that each item in the main array of 3000 objects each have a unique name? If so, you could use an NSMutableDictionary to allow access to the objects in an efficient manner by having the keys in the dictionary be the name and the values be the object instance. You could then use a separate NSMutableArray that's used merely for display purposes: it allows an ordered, sorted organization of the same objects that are stored in the NSMutableDictionary. Remember that when you add an object to an array or a dictionary, normally you're not creating a new copy, you're just retaining the existing object.