Merging NSArrays in Objective-C - objective-c

I have an NSDictionary where each key points to an array. I later want to merge all of the values into one array. Is there a way to use the API to do something more efficient than say:
NSArray *anArray = [someDictionary allValues];
NSMutableArray *newArray = [NSMutableArray array];
start outter loop on anArray
start inner loop on objects in anArray
add objectAtIndex to newArray

Just use [newArray addObjectsFromArray:anArray];

-[NSMutableArray addObjectsFromArray:]

There is some confusion in Benn Gottlieb's answer above. To clarify, he is suggesting using addObjectsFromArray instead of the inner loop, whereas Coocoo is confused because he thinks it is being suggested as a replacement for ALL the looping. (If you do this, you will indeed be left with an unflattened array of arrays as per his objection.)
Here is a reasonably elegant solution that doesn't require any explicit looping:
NSArray *anArray = [someDictionary allValues];
NSArray *flattenedArray = [anArray valueForKeyPath: #"#unionOfArrays.self"];
btw this code will leave duplicates in the flattened array. If you want to remove duplicates, use distinctUnionOfArrays instead of unionOfArrays.

Related

Objective C - NSMutableArray issue after sorting using sortedArrayUsingDescriptors

I have an NSMutableArray with custom objects.
I can addObjects, removeAllObjects and other operations with the array.
However, as soon as I sort the array via sortedArrayUsingDescriptors i cannot
perform any operations anymore such as removeallobjects. When debugging the code simply stops at this point.
Does anyone have an explanation ?
sortedArrayUsingDescriptors: returns a NSArray, not NSMutableArray.
you should use sortUsingDescriptors: to sort in place.
NSArray *sortedArray = [array sortedArrayUsingDescriptors:...];
vs.
[mutableArray sortUsingDescriptors:...];

Efficiency + safety: declare new NSArray var, or overwrite existing NSMutableArray with mutableCopy?

I need to sort a mutable array, but in this specific case when it comes time to sort, I don't need it to be mutable anymore. The sortedArrayUsingSelector: method returns an NSArray * even when called by an NSMutableArray * object. I have 3 options:
1) I can make a mutableCopy of the returned NSArray * and store it in the var I already have
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [[mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] mutableCopy];
2) I can make a new NSArray * var to hold the returned NSArray *
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
NSArray *array = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
3) I can ignore Xcode's warnings and store the returned NSArray * object in an NSMutableArray * var
NSMutableArray *mutableArray = [NSMutableArray array];
// add a bunch of stuff to the array
mutableArray = [mutableArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I realize that for most cases the difference is negligible, but considering available resources, efficiency, and safety, which would be the overall "best" option?
EDIT: I hadn't considered that the best option might be to create a non-mutable array from the mutable one before sorting it. Not sure if this would be, in fact, the best option, but something I figured I'd mention.
Since you have a mutable array already, just use the following method on NSMutableArray:
- (void)sortUsingSelector:(SEL)comparator
That way you're not recreating anything. This is likely more efficient than creating a new, sorted array from the original and then creating a mutable copy of that. Part of the point of having a mutable array in the first place is that you can change stuff around without needing to recreate it each time, so sorting is a very obvious thing to have here.
The array you get back from sortedArrayUsingSelector won't be a deep copy - it will contain the same pointers as the original array, just in a different order. These array methods in foundation will be well optimized, so I wouldn't worry about it too much.
Instead, just decide what you want. If you want a sorted NSArray * that won't look like an NSMutableArray *, just use sortedArrayUsingSelector, which returns an NSArray *
If you don't need the original unsorted array anymore, just sort the mutable array, like #Gavin suggests.

objective c arrays linked somehow

Hi i have an array which i am trying to copy then remove an element from. The issue is i think i am removing the element from the copy but it also removes the element from the original array. I have no idea what is going on. Im new to objective c so any help would be much appreciated.
NSArray *newarray = appDelegate.orginalArray;
[newarray removeObjectAtIndex: 2];
When i look at the arrays after removing the object it had removed it from both newarray and orginalArray why would this be?
Many thanks
This isn't making a copy. Both newArray and appDelegate.originalArray refer to the same object. You would need to do this:
NSMutableArray *newArray = [appDelegate.originalArray mutableCopy];
Also note that I'm using NSMutableArray and mutableCopy to ensure the copy is mutable (i.e. supports adding and removing items)
Pointers, buddy, and mutable/immutable abstractions. newarray and appDelegate.originalArray are both pointers (references) to the same immutable (unchangeable) array. You want:
NSArray *newarray = [appDelegate.orginalArray mutableCopy];

Removing Objects From NSMutableArray

I have a NSMutableArray that contains all the calendars on my system (as CalCalendar objects):
NSMutableArray *calendars = [[CalCalendarStore defaultCalendarStore] calendars];
I want to remove from calendars any CalCalendar objects whose title does not include the string #"work".
I've tried this:
for (CalCalendar *cal in calendars) {
// Look to see if this calendar's title contains "work". If not - remove it
if ([[cal title] rangeOfString:#"work"].location == NSNotFound) {
[calendars removeObject:cal];
}
}
The console is complaining that:
*** Collection <NSCFArray: 0x11660ccb0> was mutated while being enumerated.
And things go bad. Obviously it would seem you can't do what I want to do this way so can anyone suggest the best way to go about it?
Thanks,
While you can not remove items in an array that you are using fast enumeration on, you have some options:
filter the array using -filterUsingPredicate:
use index-based iteration
remove via index sets, e.g. using -indexesOfObjectsPassingTest:
build an array of the objects to remove and use e.g. -removeObjectsInArray:
As markhunte noted, -calendars doesn't neccessarily return a mutable array - you'd have to use -mutableCopy to get a mutable array which you can filter:
NSMutableArray *calendars = [[[[CalCalendarStore defaultCalendarStore]
calendars] mutableCopy] autorelease];
... or e.g. -filteredArrayUsingPredicate: for a immutable filtered copy.
NSArray *calendars = [[CalCalendarStore defaultCalendarStore] calendars];
calendars = [calendars filteredArrayUsingPredicate:myPredicate];
You can not change an array/list you are enumerating (in any language I know of). You will need to create a second list that you will add the calendars that you want to remove, to. Then iterate round the second list, removing the objects from the first. You can then dispose of the second list leaving just the original list with only the calendars you wish to keep hold of.
I noticed NSMutableArray will give the expected results from [[CalCalendarStore defaultCalendarStore] calendars]].
But the returned array from [[CalCalendarStore defaultCalendarStore] calendars]] is actually a NSArray and not an NSMutableArray. (also has a warning come up about expected structure)
This is all new to me so am I missing something here?
or is this the correct way of going about this task..
NSMutableArray *workCals= [[NSMutableArray alloc] initWithCapacity:2];
[workCals addObjectsFromArray: [[CalCalendarStore defaultCalendarStore] calendars]];
NSString *title = #"work";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"title contains[c] %#",title ];
[workCals filterUsingPredicate:predicate];
NSLog(#"workCals %#",workCals );
[workCals release];
Cheers.

Terminology question regarding looping thru an NSArray in Objective-C

When you have an NSArray and you want to evaluate and change the elements, you can't change the array from inside the loop. So, you create a mutable copy that can be changed.
code example:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", #"2", #"3", #"4", #"5", #"6", #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", #"b2", #"c3", #"e4", nil];
NSMutableArray *listHolder = list; // can't mutate 'list' within loop so create a holder
for (int i = 0; i < [list count]; i++) {
[listHolder replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
What is that second array listHolder called? I mean, what term is used to refer to an array in this context.
This is perfectly valid:
NSMutableArray *bin = [NSMutableArray arrayWithObjects:#"0", #"1", …, #"7", nil];
NSMutableArray *list = [NSMutableArray arrayWithObjects:#"a1", …, #"e4", nil];
// NSInteger should be used instead of int
for (NSInteger i = 0; i < [list count]; i++) {
[list replaceObjectAtIndex:i withObject:[bin objectAtIndex:i]];
}
You're not allowed to change the array inside a for … in or NSEnumerate loop, but using an index is perfectly valid.
What troubles me is your misunderstanding of pointers.
If it were a loop in which you weren't allowed to mutate the array this wouldn't copy the array but only the pointer to the array, effectively modifying the array you're not allowed to. (I'm not even sure if this works.)
Instead of just copying the pointer
// can't mutate 'list' within loop so create a holder
NSMutableArray *listHolder = list;
make a true copy:
NSMutableArray *copy = [[list mutableCopy] autorelease];
In case I really have to make a copy I try to name it according to its content. For example:
NSMutableArray *views;
NSMutableArray *reorderedViews = [views mutableCopy];
// reorder reorderedViews
Sometimes it's hard to find a good enough name, then I usually just use nameCopy.
In this context listHolder would be called a copy.
Your code has a bug though. This line is not actually making a copy, it is only letting listHolder and list both reference the same array object:
NSMutableArray *listHolder = list;
This would be an actual copy:
NSMutableArray *listHolder = [list mutableCopy];
Make sure that you use mutableCopy and not just copy if you want the copy to be mutable. The copy method will return immutable variants on all mutable classes such as NSMutableSet, NSMutableDictionary, and so forth.
Also as others have noted it is only inside the for (item in collection) loop that the enumerated collection can not be mutated. In a normal for (;;) mutation is perfectly ok, but can lead to strange result if the number of items in the collection changes.
There is not specific stylistic or common name for this that is universally used, it is your code afterall, and if there appropriate terms for them use them.
Having said that generally if you don't have specific names in this sort of situation then people refer to the original list as the "source" (src) and the final list as "destination" (dst), just like in a memory blitting style operation.
A temporary mutable copy of the original NSArray would be how I would refer to it.