Enumerating array of objects - ios

I have array of objects and some objects in it have the same value(for example user's guid).
I want find all object with same guide and remove all of then rather then first.
What is the best way to do it?

You can use the NSMUtableArray's removeObject method. Notice that your object should implement the isEqual method appropriately.
[NSMutableArray removeObject]
as per the description:
This method uses indexOfObject: to locate matches and then removes
them by using removeObjectAtIndex:. Thus, matches are determined on
the basis of an object’s response to the isEqual: message. If the
array does not contain anObject, the method has no effect (although it
does incur the overhead of searching the contents).

So, first of all you array need to be mutable NSMutableArray, then the process is:
consider the actual object;
check if is present another object equal to this in the other objects;
if yes, delete the equal objects include the actual.
-
NSMutableArray *arr = [NSMutableArray arrayWithArray:#[#1, #2, #3, #2, #5, #3]];
for(int i=0; i<[arr count]; i++) {
id obj = arr[i];
if([arr indexOfObject:obj inRange:NSMakeRange(i+1, [arr count]-i-1)] != NSNotFound) {
[arr removeObject:obj inRange:NSMakeRange(i, [arr count]-i)];
i--;
}
}

Related

what's the difference between [array count] and array.count in Objective-C

If I want to know the size of a NSArray, there are two similar methods I can use like:
NSArray *arr = #[#"1", #"2"];
NSInteger i = [arr count];
NSInteger j = arr.count;
So what the difference between these two methods? Will there be any performance difference or else? Thanks a lot
With [arr count]; you send the message count to the array object.
If arr.count comes to the right of some expression, you are calling the getter of the count property, which is basically the same as [arr count];
If object.someProperty comes to the left of some expression, you are calling the setter of the count property, which is basically the same as [object setSomeProperty:someValue].
Because the syntax of the getter and the sending a message to an object represent the same thing for a property (when on right side of an expression), the compiler allows you to use the . (dot) syntax even if the name of what comes right after the dot is not necessarily a getter of a property (for example count is a method of the NSArray class, but compiler does not complain if you use [arr count] or arr.count).
[arr count] and arr.count are basically the same thing. Both call obj_msg_send, the dot syntax is just syntatic sugar for [arr count].
[arr count] when you call this you are straight away accessing the getter method.
arr.count when you use .(dot) you are accessing the property of an object
but value wise both gives same count.

NSArray with objects that "might" be nil

I have 3 objects that might be or not initialized in a random order.
so, if objects "objectOne, "objectTwo", "objectThree" are initialized in this order with
myArray = [NSArray arrayWithObjects:objectOne,objectTwo,objectThree nil];
all objects get inside the array without problem but in my case objectOne, objectTwo might be nil and objectThree might not be nil, and in this case I would like myArray to return(count) 1.
if objectOne is nil but objectTwo and objectThree are not nil I want my array to return(count) 2.
In these 2 last cases my array always return nil. What would be the best approach to this?
There are no magic method can solve the problem for you, you need to build the array from NSMutableArray
NSMutableArray *array = [NSMutableArray array];
if (objectOne) [array addObject:objectOne];
if (objectTwo) [array addObject:objectTwo];
if (objectThree) [array addObject:objectThree];
arrays can't contain nil. There is a special object, NSNull ([NSNull null]), that serves as a placeholder for nil. You can put NSNull in an array, but I don't think that solves your problem either.
How about this:
Create an empty mutable array.
In 3 separate statements:
If objectOne is not nil, add it to the array
if objectTwo is not nil, add it to the array
If objectThree is not nil, add it to the array.
If you need your objects to be in random order, scramble the array afterwords:
for (int index = 0; index < array.count; index++)
{
int randomIndex = arc4random_uniform()
[array exchangeObjectAtIndex: index withObjectAtIndex: randomIndex];
}
This is known as a Fisher–Yates shuffle. (or a minor variation on Fisher-Yates, anyway.)
If you're doing this rarely and you aren't trying to make things neat, you can, of course, use a mutable array and either add or don't add the items one at a time in code, depending on whether they are nil.
If you're doing this frequently and you want a syntax that looks similar to the array literal notation, you can take advantage of the C preprocessor and C arrays to create a smarter NSArray class constructor that handles nil:
#define NSArrayWithCArray(array) \
[NSArray arrayWithCArray:cArray count:sizeof(cArray) / sizeof(cArray[0])];
id cArray[] = {
object1,
object2,
object3,
...
};
NSArray *array = NSArrayWithCArray(cArray);
and then define a method on NSObject that walks through the C array programmatically, dropping any nil values.
+ (NSArray *)arrayWithCArray:(__strong id[])cArray count:(NSUInteger)count {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
for (__strong id *item = cArray; item < cArray + count; item++) {
if (*item != nil) {
[array addObject:*item];
}
}
return array;
}
Note: The code above is untested, but at least close enough to give you an idea of how to do it. :-)

How to check if a NSArray has the values of another array

I have my array unique that is my main array and my array kind. I need to check that only 1 value of kind is present in the array unique. Then if there is more than 1 value of the array kind in unique I need to unset all values but the first one used in the array.
The further i got to achieve this is with the following code but I can not store the indexpath of the found object to do a later comparison. xcode says "bad receiver type nsinteger"
could anyone help me to achieve this?
kind = #[#"#Routine",#"#Exercise",#"#Username"];
NSMutableArray *uniqueKind = [NSMutableArray array];
for (NSString* obj in kind) {
if ( [unique containsObject:obj] ) {
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [i intValue]];
}
}
An NSInteger is like an int, so you can't send it a message ([i intValue]). Also, you can't add an NSInteger to an array without making it an NSNumber or some other object type. You can do it like this:
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [NSNumber numberWithInteger:i]];
Also (without understanding what you're doing) you might want to use an NSSet instead of an array. And you can combine a couple of calls:
NSUInteger i = [unique indexOfObject:obj];
if ( i != NSNotFound ) {
[uniqueKind addObject:[NSNumber numberWithInteger:i]];
}
I'm not sure if it would solve your problem, but have you considered using sets (or mutable variation) instead of arrays? They ensure uniqueness and they allow you to check for intersection/containment. See the NSSet class reference.
You have to add objects to the NSMutableArray, not the actual intValue. Try converting teh integer to a NSNumber first.
[uniqueKind addObject: [NSNumber numberWithInt:i]];
instead.
(edited )

Regarding MPMediaItemCollection items lastObject

I'm setting N items in an MPMediaItemsCollection, where some items aren't unique (the collection represents a playlist, where the same song might appear twice).
- (void)setLastSongWithItemCollection:(MPMediaItemCollection *)itemCollection
{
[_musicPlayer setQueueWithItemCollection: itemCollection];
NSLog(#"itemCollection count %u", itemCollection.count);
NSLog(#"itemCollection lastObject index: %u", [itemCollection.items indexOfObject:itemCollection.items.lastObject]);
_musicPlayer.nowPlayingItem = [[itemCollection items] lastObject];
}
In one example, I'm generating itemCollection with 4 songs, where the last song is the first one repeated. If I attempt to get the last object in the list, _musicPlayer will always play the first item.
The first NSLog prints "itemCollection count 4" clearly indicating there are four items in the items array. However, the second NSLog prints "itemCollection lastObject index:0", indicating that items.lastObject does not return the object and the last index, but rather the first one where that same media item occurs.
Can anyone shed some light on this?
CORRECTION: As pointed out, indexOfObject: is not a valid test for returning the index of a repeated object
Still, _musicPlayer.nowPlayingItem = [[itemCollection items] lastObject] sets the first instance identical to lastObject to the media player. It seems the nowPlayingItem setter calling indexOfObject: on itemCollection. Workarounds?
Follow-up
It seems there is no way to select an index in the queue of an MPMediaPlayer, and setNowPlayingItem: calls indexOfObject: To find a selected media item. The result is that indexOfNowPlayingItem will return the index of the first instance identical to nowPlayingItem in the queue. In my case, this affects the visual representation of the queue that I have in my app (it's a scrollview with a panel for each song). It's possible that MPMediaItemCollection was not intended for use with playlists where MPMediaItems can occur more than once. I will fill a bug report in the interest of getting more information on the subject.
This really has nothing to do with MPMediaItemsCollection specifically. Rather, it's just the behavior of NSArray.
As the documentation for -[NSArray indexOfObject:] states, it "returns the lowest index whose corresponding array value is equal to a given object." In this case, equality is determined by sending each object an -isEqual: message.
So, if you have an array:
NSArray *array = #[#"A", #"B", #"C", #"A"];
You'll see the following:
[array indexOfObject:#"A"] // returns 0
[array indexOfObject:array[3]] // also returns 0
[array indexOfObject:[array lastObject]] // also returns 0
Essentially, -[NSArray indexOfObject] looks like this:
- (void)indexOfObject:(id)object
{
for (NSUInteger i=0; i<[self count]; i++) {
if ([self[i] isEqual:object]) return i;
}
return NSNotFound;
}
If you want to know the indexes for all matching objects in the array, use -[NSArray indexesOfObjectsPassingTest:]
NSArray *array = #[#"A", #"B", #"C", #"A"];
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [obj isEqual:#"A"];
}];
NSLog(#"Indexes: %#", indexes);
>>Indexes: <NSIndexSet: 0x7fbc9b40b170>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
If you really need to know which index should be associated with your use of a non-unique item in the playlist, you'll have to keep track of that yourself by storing it when you retrieve the media item in the first place.

What's the standard convention for creating a new NSArray from an existing NSArray?

Let's say I have an NSArray of NSDictionaries that is 10 elements long. I want to create a second NSArray with the values for a single key on each dictionary. The best way I can figure to do this is:
NSMutableArray *nameArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSDictionary *p in array) {
[nameArray addObject:[p objectForKey:#"name"]];
}
self.my_new_array = array;
[array release];
[nameArray release];
}
But in theory, I should be able to get away with not using a mutable array and using a counter in conjunction with [nameArray addObjectAtIndex:count], because the new list should be exactly as long as the old list. Please note that I am NOT trying to filter for a subset of the original array, but make a new array with exactly the same number of elements, just with values dredged up from the some arbitrary attribute of each element in the array.
In python one could solve this problem like this:
new_list = [p['name'] for p in old_list]
or if you were a masochist, like this:
new_list = map(lambda p: p['name'], old_list)
Having to be slightly more explicit in objective-c makes me wonder if there is an accepted common way of handling these situations.
In this particular case Cocoa is not outdone in succinctness :)
NSArray *newArray = [array valueForKey:#"name"];
From the NSArray documentation:
valueForKey:
Returns an array containing the
results of invoking valueForKey: using
key on each of the receiver's objects.

Resources