taking out objects from NSMutableArray - objective-c

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.

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.

Filling NSMutableArray for later use in obj-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.

Why does accessing my NSMutableArray become faster once I've added more objects to it?

Something very odd is going on. I populate my array as follows:
self.workingWithItemCollectionArray = [NSMutableArray arrayWithCapacity:self.itemCollectionArray.count];
self.workingWithItemCollectionArray = [[self.itemCollectionArray mutableCopy]autorelease];
It take a mutable copy of the original NSArray and pass it to the NSMutableArray. When accessing the information contained in this array by the click of a UIButton, there is a slight delay in retrieving the information.
But when I then change the original array to add more items, and then pass this onto the mutable array:
NSMutableArray *editedOriginalArray = [NSMutableArray arrayWithArray:self.itemCollectionArray];
[editedOriginalArray addObjectsFromArray:extraObjectsToAdd];
self.itemCollectionArray = [NSArray arrayWithArray:editedOriginalArray];
self.workingWithItemCollectionArray = [NSMutableArray arrayWithCapacity:self.itemCollectionArray.count];
self.workingWithItemCollectionArray = [[self.itemCollectionArray mutableCopy]autorelease];
It is then after this that I am able to press the button and information is accessed instantly (whereas before the button would stay pressed for a very short time).
Any ideas on why this could be?
It has to do with how NSMutableArray is implemented vs NSArray.
Because NSArray is immutable, the objects are literally internally stored in an array, e.g.:
id *objects = malloc(sizeof(id) * count);
However, when you deal with NSMutableArray, you are dealing with instead, a linked list, as NSMutableArray expects many modifications on the array. So, the lookup on a linked list is much longer, because your objects are not stored in a way where there is a set distance in memory between them.
For more information on linked lists, check here.

Creating an array from properties of objects in another array

Is there any convenient way to take an array/set of objects and create a new array/set containing some property of each item in the first array?
For example, an array contains Car objects. I need an array of licensePlates, where each car has an NSObject car.licensePlate.
Currently I just iterate through the first array adding objects to my mutable results array, but was wondering if there is an instantiation method that exists for this (checked the docs for NSArray).
This will return an array containing the value of licensePlate from each item in the myCars array:
NSArray *licensePlates = [myCars valueForKeyPath:#"licensePlate"]
If you want only unique items (for example), you can do something like this:
NSArray *licensePlates = [myCars valueForKeyPath:#"#distinctUnionOfObjects.licensePlate"];
For more possibilities, see the Collection Operators documentation in the Key-Value Coding Programming Guide.

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.