Instantiate an NSArray passing a mutable array changes array to mutable? - iphone

If I instantiate an NSArray passing it an NSMutableArray, does it become a NSArray or does it just appear to be one.
i.e. is the mutable array eventually released
- (NSArray *)getObjectivesWithPerspective:(Perspective *)perspective
{
NSMutableArray *result = [NSMutableArray array];
for (ObjectiveManagedObject *objective in self.objectives)
{
if (objective.perspective.objectID == perspective.objectID) {
[result addObject:objective];
}
}
return [NSArray arrayWithArray:result];
}

See this answer: https://stackoverflow.com/a/1392051/254422
Basically, it copies the objects into a new, autoreleased, immutable NSArray.
The NSMutableArray created using +array is autoreleased.

You can find some info about this here: what does -arrayWithArray actually DO?. But the array you get back should be immutable. NSArray will copy the references into an immutable array.
If in doubt do a test. Get an array back from the method, cast it as a NSMutableArray and try to add an object. You should get an error when you do this.

As NSMutableArray is a subclass of NSArray, you can return result directly without the need to create a new NSArray object:
- (NSArray *)getObjectivesWithPerspective:(Perspective *)perspective
{
NSMutableArray *result = [NSMutableArray array];
for (ObjectiveManagedObject *objective in self.objectives)
{
if (objective.perspective.objectID == perspective.objectID) {
[result addObject:objective];
}
}
return result;
}

Related

error in array cleaning method

I am attempting to use this array cleaning method, and there seems to be an error. I can't spot it, I know the array goes in with 3116 items, comes out with 3116 (and I know for a fact there are three duplicates.
Please advice, thanks!
-(NSArray*) removeDuplicates:(NSArray*)inputArray{
NSMutableArray *arrayToClean = [NSMutableArray arrayWithArray:inputArray];
for (int i =0; i<[arrayToClean count]; i++) {
for (int j=(i+1); j < [arrayToClean count]; j++) {
if ([[arrayToClean objectAtIndex:i] isEqual:[arrayToClean
objectAtIndex:j]]) {
[arrayToClean removeObjectAtIndex:j];
j--;
}
}
}
NSArray *arrayToReturn = [NSArray arrayWithArray:arrayToClean];
return arrayToReturn;
}
NSSet will make this a lot easier:
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSSet *unique = [NSSet setWithArray:inputArray];
return [unique allObjects];
}
Please note that a set has no guaranteed order. If you need the objects in the array to be in a specific order then you should sort the resulting array as needed.
It may also be appropriate to use an NSSet instead of the original array, then you don't need to worry about duplicates at all. But this depends on the other needs of your array.
Hey You can use another alternative for this.You can use the NSSet here for this task.
NSSet declares the programmatic interface for static sets of distinct objects
You can use sets as an alternative to arrays when the order of elements isn’t important and performance in testing whether an object is contained in the set is a consideration—while arrays are ordered, testing for membership is slower than with sets.
You Just need To call below method.
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSSet *finalData = [NSSet setWithArray:inputArray];
return [finalData allObjects];
}
If really face any problem in above way of cleaning ducplicates then you can try another Alterantive.
-(NSArray *)removeDuplicates:(NSArray *)inputArray {
NSMutableArray *inputArray1=[NSMutableArray arrayWithArray:inputArray];
NSMutableArray *finalARray=[[NSMutableArray alloc]init];
for (id obj in inputArray1)
{
if (![finalARray containsObject:obj])
{
[finalARray addObject: obj];
}
NSLog(#"new array is %#",finalARray);
}
return finalARray;
}
I hope it may help you ...
Here is a helper function I had in a previous project to do the exact same thing
- (NSMutableArray *)removeDuplicates:(NSMutableArray *)sortedArray{
NSMutableSet* valuesAdded = [NSMutableSet set];
NSMutableArray* filteredArray = [[NSMutableArray alloc] init];
NSString* object;
/* Iterate over the array checking if the value is a member of the set. If its not add it
* to the set and to the returning array. If the value is already a member, skip over it.
*/
for (object in sortedArray){
if (![valuesAdded member:object]){
[valuesAdded addObject:object];
[filteredArray addObject:object];
}
}
return filteredArray;
}

NSMutableArray replace object

I try to find object in my array and if success I need to replace object from my array to new object
for (id existingSig in allSignature)
if ([[existingSig objectForKey:#"SignatureName"] isEqualToString:[item objectForKey:#"name"]])
{
[allSignature removeObject:existingSig];
[allSignature addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"SignatureIsRich", [item objectForKey:#"name"], #"SignatureName", generatedID, #"SignatureUniqueId", nil]];
}
I have error 'NSCFArray: 0x100551f10> was mutated while being enumerated'
As others have said, you cannot mutate a MutableArray while it is being Enumerated. You could handle it by having two arrays of what to remove and what to add after the loop.
NSMutableArray *remove = [NSMutableArray array];
NSMutableArray *add = [NSMutableArray array];
for (id existingSig in allSignature){
if ([[existingSig objectForKey:#"SignatureName"] isEqualToString:[item objectForKey:#"name"]])
{
// Add to the array of objects to be removed
[remove addObject:existingSig];
// Add to the array of objects to be added
[add addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"1", #"SignatureIsRich", [item objectForKey:#"name"], #"SignatureName", generatedID, #"SignatureUniqueId", nil]];
}
}
[allSignature removeObjectsInArray:remove]; // Remove objects
[allSignature addObjectsFromArray:add]; // Add new objects
The easiest way is to make a copy and iterate over that, then modify the original.
I have a map array method I have in a NSArray Category for this very purpose
- (NSArray *) cw_mapArray:(id (^)(id obj))block
{
NSMutableArray * cwArray = [[NSMutableArray alloc] init];
[self cw_each:^(id obj, NSUInteger index, BOOL *stop) {
id rObj = block(obj);
if (rObj) {
[cwArray addObject:rObj];
}
}];
return cwArray;
}
this way I can get a new array and then just change the array with the new array. You can change cw_each to enumerateObjectsUsingBlock:. Basically this is simple, if you want to map the object to the new array you just return the object as is in the block, otherwise modify it and return that or if you don't want to map the object to the new array then return nil. Its not very much code and works wonderfully.

NSArray of united Arrays

I have an NSArray of objects called MMPlace, which has NSArray of MMProduct objects.
How do I get a united NSArray of all MMProduct objects that my Array of MMPlace object contains? Something like NSArray *arr = [array valueForKeyPath:#"#unionOfObjects.products"]; would be nice, though this specific example doesn't work.
You can do this with #unionOfArrays. The bit you were missing is that because the arrays are directly nested, the key on the right of the collection operator must be self:
NSArray *nestedValues = #[#[#1, #2, #3], #[#4, #5, #6]]
NSArray *flattenedValues = [nestedValues valueForKeyPath:#"#unionOfArrays.self"];
// flattenedValues contains #[#1, #2, #3, #4, #5, #6]
Create an NSMutableArray, loop through your original array and call addObjectsFromArray: with each subarray.
I don't think there is an off-the-shelf method that does what you need, but you can easily "flatten" your array in a for loop, and hide the method in a category:
Edit: added a category.
#interface NSArray (flatten)
-(NSArray*) flattenArray;
#end
#implementation NSArray (flatten)
-(NSArray*) flattenArray {
// If inner array has N objects on average, multiply count by N
NSMutableArray *res = [NSMutableArray arrayWithCapacity:self.count];
for (NSArray *element in self) {
[res addObjectsFromArray:element];
}
return res;
}
#end
For this task there is the #unionOfArrays collection operator.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html#//apple_ref/doc/uid/20002176-BAJEAIEE
Here is how I implemented a "flatten" category method that will work on any tree structure. It will take a tree of arbitrary depth and merge it into one long array in order.
- (NSArray *) flattenWithBlock:(NSArray *(^)(id obj))block {
NSMutableArray *newItems = [NSMutableArray array];
for(id subObject in self)
{
if([subObject isKindOfClass:[NSArray class]])
{
[newItems addObjectsFromArray:[subObject flatten:block]];
}
else
[newItems addObject:subObject];
}
return newItems;
}
You could then write a convenience category method which handles the case you described above. This wrapper method will flatten a nd array into a 1d array.
- (NSArray *) flattenArray {
NSArray *newItems = [self flattenWithBlock:^NSArray *(id obj) {
return obj;
}];
return newItems;
}
#interface NSArray (Flatten)
-(NSArray*)flattenedArray;
#end
#implementation NSArray (Flatten)
-(NSArray*)flattenedArray {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];
for (id thing in self) {
if ([thing isKindOfClass:[NSArray class]]) {
[result addObjectsFromArray:[(NSArray*)thing flattenedArray]];
} else {
[result addObject:thing];
}
}
return [NSArray arrayWithArray:result];
}
#end

how can I save NSMutableArray into NSMutableArray so that previous data in NSMutableArray remain same?

I want to know that how can I add NSMutableArray in to an NSMutableArray so that previous data should not lost, and new data will be added on next indexes.
If you don't understand it then you can ask again to me,
I will appraise the right answer.
my code is as below
-(void)setArray1:(NSMutableArray *)arrayValueFromNew
{
self.myArray=arrayValueFromNew;
myArray2 = [[NSMutableArray alloc] initWithArray:arrayValueFromNew];
for(int i=0;i<[myArray2 count];i++)
{
[myArray addObject:[myArray2 objectAtIndex:i]];
}
}
In your code, myArray and myArray2, both have same objects as you are assigning the arrayValueFromNew array to both. So it kind of doesn't make sense.
But to answer your question 'how to add one array to another?' do :
[mutableArray1
addObjectsFromArray:array2];
EDIT:
this is how your method should look
-(void)setArray1:(NSMutableArray *)arrayValueFromNew
{
if(!self.myArray)
{
self.myArray = arrayValueFromNew;
}
else
{
[self.myArray addObjectsFromArray:arrayValueFromNew];
}
}
Your 'myArray must be initialized. You can initialize it in viewDidLoad or init:
self.myArray = [[NSMutableArray alloc]
initWithCapacity:1];
NSMutableArray *array1 = [NSMutableArray array], *array2 = [NSMutableArray array];
// add some objects to the arrays
[array1 addObjectsFromArray:array2];
//array1 now contains all the objects originally in array1 and array2
This will work,
NSMutableArray *mutarr=[[NSMutableArray alloc]initWithArray: array1]
It looks like you just want a new copy of the old array. There is a handy function for that
NSMutableArray *newArray = [oldArray mutableCopy];
Remember that you've used copy in getting this array so you are responsible for managing the memory of newArray
EDIT
What is your code doing?
-(void)setArray1:(NSMutableArray *)arrayValueFromNew //1
{
self.myArray=arrayValueFromNew; //2
myArray2 = [[NSMutableArray alloc] initWithArray:arrayValueFromNew]; //3
for(int i=0;i<[myArray2 count];i++)
{
[myArray addObject:[myArray2 objectAtIndex:i]]; //4
}
}
This looks like a setter for a property array1
You are setting the property 'array' to arrayValueFromNew. Since I don't know whether this property has been declared with retain or copy I don't know whether array is a pointer to arrayValueFromNew or a pointer to a copy of arrayValueFromNew
You set myArray2 to be a new array that contains the objects of arrayValueFromNew
For each object in myArray2 (which are the objects from arrayValueFromNew. see point 3) you add this object to myArray. Assuming myArray is an NSMutableArray it started with the objects from arrayValueFromNew which you have now added again. It contains each item in arrayValueFromNew twice.

how to detect if an array isnt empty?

I am trying to detect if an array isn't empty in order to be able to do a certain call.
I tried using if (![array ==nil]) however that doesn't compile.
I'm sure there is a really easy explanation to this.
Update
If array is empty I want to do this:
array = [[NSMutableArray alloc]init];
If it has an object I want to do this:
array = [[userDefaults arrayForKey:#"MyFavorites"] mutableCopy];
If you declared it but did not assign anything to it at all:
NSMutableArray *array;
Then the array will be nil, meaning it isn't there at all so you can't say if it's empty or not, so you can't check anything.
If you did assign something to it, and you want to find out if the existing array is empty or not, that would depend on how you created it first.
If the array was assigned from some convenience method, it's autoreleased, so just do this:
if ([array count] == 0) {
array = [[NSMutableArray alloc] init];
} else {
array = [[userDefaults arrayForKey:#"MyFavorites"] mutableCopy];
}
If the array was assigned from an init or copy method, or it was retained previously, store the count in a temporary variable, release the array and use the temporary variable to decide what to do:
NSInteger count = [array count];
[array release];
if (count == 0) {
array = [[NSMutableArray alloc] init];
} else {
array = [[userDefaults arrayForKey:#"MyFavorites"] mutableCopy];
}
In your case I'd always use without differentation
array = [[userDefaults arrayForKey:#"MyFavorites"] mutableCopy];
and set the default value in the user defaults to an empty array right away at program start before accessing the defaults (from Apple's example):
+ (void)initialize{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary
dictionaryWithObject:[NSArray array] forKey:#"MyFavorites"];
[defaults registerDefaults:appDefaults];
}
See Apple's doc on this.
Supposing you are talking about NSArray, if myArray has not been properly alloced+initialized (what you are trying to check) its reference will be nil, so you can do:
if(myArray) //or even if(myArray != nil) since myArray will be a pointer
{
//properly inizialized
}
else
{
//not properly inited
}
If it's been inited on the other hand, you can test its emptiness by checking the count property which returns the number of elements it contains
if([myArray > 0])
//there is at least one element
}
else
{
//no elements
}
you can use count function of NSArray. it will work on NSMutableArray too....
syntext will be,
int ct=[array count];
ct will have number of items in array.
if it us empty it will be Zero