I have an NSArray of custom objects. Consider that the custom objects have a PageNumber property. I would like to filter my NSArray with a condition like "customObject.PageNumber is distinct".
I know I can loop through the array and eliminate object with duplicate pageNumbers. But is there any easy way to do it? I have tried,
[myarray valueForKeyPath:#"distinctUnionOfObjects.pageNumber"];
It is giving me the unique page numbers (like 7, 8, 9). But I want the custom object itself rather than just page numbers. Can any predicate help me?
I have created a simple library, called Linq to ObjectiveC, which is a collection of methods that makes this kind of problem much easier to solve. In your case you need the Linq-to-ObjectiveC distinct method:
NSArray* itemsWithUniquePageNumbers = [items distinct:^id(id item) {
return [item pageNumber];
}];
This returns an array of objects, each one with a unique page number.
Yes, that is possible with the help of NSPredicate
customObject=[(NSArray*)[myArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self.PageNumber==%d",pageNumber]] lastObject];
//pageNumber is an integer
The filtered array is an NSArray of your custom objects which is the result of filtering using the predicate. Since your page number is unique, it will return only an array of one object. We get that by passing lastObject message to it.
Refer:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/predicates.html#//apple_ref/doc/uid/TP40001798-SW1
Related
I have an array which is a parsed xml feed which i want to add to another array using the code....
int insertIdx = [blogEntries count];
for (RSSItem *nextItem in feedItems) {
[blogEntries insertObject:nextItem atIndex:insertIdx];
//[blogEntries addObject:nextItem];
insertIdx += 1;
}
For some reason all of the objects in the blogEntries array all have an index of 0, when i slog them all out using...
for (RSSItem *nextItem in blogEntries)
NSLog(#"title - %#, pos - %i", nextItem.title, [blogEntries IndexOfObject:nextItem]);
do you know why the index might not be updating?
Any help would be appreciated
Did you try to use addObject: instead of insertObject:atIndex?
Are you sure that objects are different as:
indexOfObject:
Returns the lowest index whose corresponding array value is equal to a given object.
I was not able to refrain myself On the Topic of adding many item at once into a NSMutableArray, I do prefer to use this method :
- (void)addObjectsFromArray:(NSArray *)otherArray
it remove that useless for loop. (unless you need to do some other work in that loop)
And as of your problem, viperking have a big hint about a possible problem you may be facing. (if that is the case you may need to validate your isEqual: method.
I have an array of objects which have an enum as one of their properties, I would like to obtain a filtered array based upon the value of the enum i.e. the returned array contains only objects which have a specified enum value.
I was wondering if KVO could be used as a tidy way of doing this, but haven't found anything suggesting it is?
You can do this by filtering the array using a predicate:
NSArray * filteredArray = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"nameOfProperty == %d", theEnumValue]];
The string for the predicate names the property you're interested in, the value to which it should be compared, and the relationship the two must have for the predicate to evaluate as true.
Based on the data model below
And based on user input I create a NSSet of managedObjects of entity Tag called selectedTags.
My problem:
[NSPredicate predicateWithFormat:#"ANY entryTags IN %#", selectedTags];
... this will return any Entry with at least one entryTag that is in the selectedTags set.
I want something along the lines of:
[NSPredicate predicateWithFormat:#"ALL entryTags IN %#", selectedTags];
... notice the only change is the "ANY" to "ALL". This illustrates what I want, but does not work.
To formulate the outcome I expect:
I'm looking for a solution that will return only Entries who's entryTags are all in the selectedTags list (but at the same time, if possible, not necessarily the other way around).
To further illustrate:
(tag)Mom
(tag)Dad
(tag)Gifts
(entry)she is a she.....(tag)mom
(entry)he is a he........(tag)dad
(entry)gifts for mom...(tags:)mom, gifts
(entry)gifts for dad.....(tags:)dad, gifts
If selectedTags contains "mom" and "gifts", then the entry "gifts for dad" will show up, since it has the tag "gifts". I'd rather have it not show :)
This is the definite answer so far:
[NSPredicate predicateWithFormat:#"SUBQUERY(entryTags, $tag, $tag IN %#).#count = %d", selectedTags, [selectedTags count]];
B-E-A-U-T-I-F-U-L.
Thanks to Dave DeLong.
How about using a compound predicate? As I understand you want to return all Entries that match a list of tags not just any of them. One approach would be to create a predicate for each tag, then AND them together using a compound predicate.
NSMutableArray *predicates = [[NSMutableArray alloc] init];
for (Tag *tag in selectedTags) {
[predicates addObject:[NSPredicate predicateWithFormat:#"ANY entryTags.tagName MATCHES %#", tag.tagName]];
}
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
This should achieve want you want. Then just set this predicate on your request.
You can't do what you want with a predicate.
The ANY and ALL operators apply to the entity being tested (in this case Entry) and not the contents of the collection (selectedTags). Either operator will return an Entry object that matches any single element of the collection. The ANY operator will return the first match it finds while the ALL operator will return all matches. In neither case will they return an entry that matches every element in the provided collection.
(It also looks like you are trying to use actual Tag objects in selectedTags. That will most likely not work either because object compares on classes without dedicated comparison methods usually fail. It is also slow. You need to compare attributes in predicates.)
Since you already have the Tag objects you want, to find the candidate related Entity objects, you just have to walk the Tag.taggedEntries relationship. Then you have to find the intersection of all the sets of Entity object to find only those Entity objects that are related to every selected Tag bject. Since there isn't an intersect collections operator, you need a loop.
if ([selectedEntries count]>=2) {
NSMutableSet *intersectEntries=[[NSMutableSet alloc] initWithCapacity:1];
for (int i=1; i<[selectedTags count]; i++) {
if ([intersectEntries count]==0) {
[intersectEntries unionSet:[[selectedEntries objectAtIndex:(i-1)] valueForKey:#"taggedEntries"]];
}
[intersectEntries intersectSet:[[selectedEntries objectAtIndex:i] valueForKey:#"taggedEntries"]];
}
}
(Note: I didn't test this but it should work.)
Now intersectEntries should contain only those Entry objects that are related to every selected tag.
I realized I could give something back here for the guidance that I have previously gotten. By using the code TechZen supplied I was able to come up with the following -- and for me highly valued -- piece of code:
- (NSArray *)unionSetOfObjectsForObjects:(NSArray *)objects {
NSMutableSet *unionSetOfObjects = [NSMutableSet set];
if (objects.count)
[unionSetOfObjects unionSet:[[objects objectAtIndex:0] valueForKey:#"key"]];
//unionSetOfObjects = [[objects objectAtIndex:0] valueForKey:#"key"];
if (objects.count > 1)
for (id object in objects)
[unionSetOfObjects intersectSet:[object valueForKey:#"key"]];
return unionSetOfObjects.allObjects;
}
If it is not immediately obvious what this code does:
It collects all the values (in my case objects) for the key key on all of the objects provided in the objects array.
This code just... tastes good, doesn't it?
The simplest way to do this:
[NSPredicate predicateWithFormat:#"entryTags IN %#", selectedTags];
You don't need the ALL clause. It's also documented here:
Predicate Programming guide
And as you can see in this post the user does it successfully (read the comments to the original question)
NSPredicate iPhone 3.2 SDK Core Data “IN clause”...
I got a NSMutableArray which I want to add strings to. I need to check if that string already exists on the array before adding ths string to it. Does anyway know a good way to do it?
Thanks!
If order doesn't matter, the simplest way would be to switch to using an NSMutableSet. Sets are unordered and NSSets and NSMutableSets can contain each object only once. Only a counted set (NSCountedSet) can contain the same object multiple times.
If order does matter, continue using NSMutableArray, and use -containsObject: to check that the object is not already there before adding it.
Edit: And as of Lion, we now have NSOrderedSet and NSMutableOrderedSet! Chocolate in my peanut butter! Peanut butter in my chocolate!
Like an unordered set, you get fast membership testing, the prohibition of duplicates, and, of course, set operations (union, intersection, etc.). Like an array, you get ordered access, including both reliable-order enumeration and access by index.
So, if you need to reject duplicates but at the same time preserve order, Lion's NS{,Mutable}OrderedSet classes are for you.
If you just want to do what you stated once, just use containsObject:, as in
if (![myArray containsObject:theObject]) [myArray addObject:theObject];
Note that this does a linear search through the array, and thus isn't appropriate if you're going to be using this operation a lot. If you are, and you don't need ordering, you should use NSSet. If you do need ordering, you could use both an NSArray and NSSet that are kept in sync (e.g. always add/delete the same object from both collections at the same time).
I preferred the NSPredicate which describe in here
In short
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"Ben", #"Adam", #"Melissa", nil];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF MATCHES[c] %#", your_search_key];
NSArray *beginWithB = [array filteredArrayUsingPredicate:bPredicate];
if ([beginWithB count] > 0)
{
//Handle duplicate
}
I want to load a client object and then pull their related purchase orders based on whether they have been placed or not, purchase orders have an IsPlaced BOOL property.
So I have my client object and I can get all purchase orders like this, which is working great:
purchaseordersList =[[myclient.purchaseorders allObjects] mutableCopy];
But ideally I would actually like 2 array's - one for each order type: IsPlaced=YES and IsPlaced=NO
How do I do that here? Or do I need to do another fetch?
First, there is no reason to be turning the set into an array unless you are sorting it and there is no reason to be turning that array into a mutable array. Did you get that from some example code?
Second, you can filter an array or a set by using a predicate so you can create two sets (or arrays) easily via:
NSSet *placed = [[myclient purchaseorders] filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"isPlaced == YES"]];
NSSet *notPlaced = [[myclient purchaseorders] filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"isPlaced == YES"]];
If you are wanting to use this for a UITableView then look into a NSFetchedResultsController instead. It will save you a LOT of boiler-plate code.
Do you remember what example code you got that from? Been seeing that -mutableCopy a lot lately and would love to quash it. :)