Little diifficult to explain but I am trying to use NSPredicate for filtering an array with custom NSManagedObject by ids. I have a server that can send update, delete or add new objects, and I need to control if those objects from the JSON file already exist, if exist just update them or insert to core data if not.
I am using this predicate now :
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"storeId != %#", [jsonFile valueForKey:#"Id"];
Where jsonFile contains unparsed Store objects. But with this predicate, it will give me a huge array, since one id will be unlike some storeId, and next id will match.
Json file is some sort of this :
"Stores":[{
"id":1,
"name":"Spar",
"city":"London"
}
{
"id":2,
"name":"WalMart",
"city":"Chicago"
}];
I am not sure if I understand correctly what you are trying to achieve, but perhaps you can use the following:
NSArray *jsonFile = /* your array of dictionaries */;
NSArray *idList = [jsonFile valueForKey:#"id"]; // array of "id" numbers
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NOT(storeId IN %#)", idList];
This will give all managed objects that have a storeId that is not equal to any of the ids in the jsonFile array.
The syntax of the predicate is probably off - someone else may suggest a fix - but if you have an array, why not use
- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
since its much easier:
NSInteger textID = ... // you set this
NSInteger idx = [myArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop))
{
NSInteger objIdx = [obj objectForKey:#"id"] integerValue]; // integerValue works for both NSNUmbers and NSStrings
if(objIdx == testID) {
return YES;
*stop = YES;
}
}
Related
How to search an NSSet or NSArray for an object which has an specific value for an specific property?
Example: I have an NSSet with 20 objects, and every object has an type property. I want to get the first object which has [theObject.type isEqualToString:#"standard"].
I remember that it was possible to use predicates somehow for this kind of stuff, right?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type == %#", #"standard"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];
id firstFoundObject = nil;
firstFoundObject = filteredArray.count > 0 ? filteredArray.firstObject : nil;
NB: The notion of the first found object in an NSSet makes no sense since the order of the objects in a set is undefined.
You can get the filtered array as Jason and Ole have described, but since you just want one object, I'd use - indexOfObjectPassingTest: (if it's in an array) or -objectPassingTest: (if it's in a set) and avoid creating the second array.
Generally, I use indexOfObjectPassingTest: as I find it more convenient to express my test in Objective-C code rather than NSPredicate syntax. Here's a simple example (imagine that integerValue was actually a property):
NSArray *array = #[#0,#1,#2,#3];
NSUInteger indexOfTwo = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return ([(NSNumber *)obj integerValue] == 2);
}];
NSUInteger indexOfFour = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return ([(NSNumber *)obj integerValue] == 4);
}];
BOOL hasTwo = (indexOfTwo != NSNotFound);
BOOL hasFour = (indexOfFour != NSNotFound);
NSLog(#"hasTwo: %# (index was %d)", hasTwo ? #"YES" : #"NO", indexOfTwo);
NSLog(#"hasFour: %# (index was %d)", hasFour ? #"YES" : #"NO", indexOfFour);
The output of this code is:
hasTwo: YES (index was 2)
hasFour: NO (index was 2147483647)
NSArray* results = [theFullArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF.type LIKE[cd] %#", #"standard"]];
For example:
I have two NSMutableArray. One #[1,2,3,4,5,6,7]. Other have objects like
#[
#{#idObjectToSearch":1, #"name":#"aaaaa", #"surname": #"bbbbb"}, #{#idObjectToSearch":2, #"name":#"aaaaa", #"surname": #"ccccc"},
...
#{#idObjectToSearch":100, #"name":#"aaaaa", #"surname": #"cccdcd"}
];
So how I could extract needed objects from second array more effective way?
You need to use NSPredicate with your second array.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idObjectToSearch IN %#", firstArray];
//In above predicate instead of passing `1` you need to pass object from first array that you want.
NSArray *filterArray = [secondArray filteredArrayUsingPredicate:predicate];
//Now access Array objects
if (filterArray.count > 0) {
NSLog(#"%#",filterArray);
}
You can do it like this
NSMutableArray * arrSorted = [NSMutableArray new];
for(int i=0;i<arr.count;i++) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idObjectToSearch == %#", firstArray[i]];
NSUInteger index = [secondArray indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [predicate evaluateWithObject:obj];
}];
if (index != NSNotFound) {
[arrSorted addObject:[arrM objectAtIndex:index]];
}
}
arrSorted will contain your sorted data
I have an NSMutableArray which contains several NSMutableDictonaryobjects. I am using NSPredicate to find out that dictionary with specific key exist or not in the array
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%# IN self.#allKeys", users.cellNum];
NSArray *predResult = [[[self appDelegate]mainArray] filteredArrayUsingPredicate:pred];
BOOL success = predResult.count == 0;
if(success) {
}
its working fine but in predResult I am getting the whole object and it will become memory overhead as the size grows with time.
So I just want to know the dictionary with specific key exist in array or not. I don't want to fetch any object using filteredArrayUsingPredicate.
NSUInteger rowindex = [[[self appDelegate]mainArray] indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return [pred evaluateWithObject:obj];
}];
This will return the integer and
if(rowindex==0|| (rowindex>0&&rowindex<[[self appDelegate]mainArray].count)) {
// data found
}
If data is found rowindex will be greater than zero and less than array count
I am trying to find the tags inside a NSDictionary inside myAr that matches the criteria of str and I want the result that has only those exact arrays no more nor less. In this example I want only the 2nd NSDictionary of myAr.
I though of trying to achieve this by using a predicate but that always returns empty when i use arrays.
I am trying to filter using an array but this is not working. I was wondering if anyone could tell me what i am doing wrong and how could i achieve my objective. thanks in advance
NSArray * myAr = #[ #{ #"tags": #[#"one",#"two",#"three"],
#"number": #"4"
},
#{ #"tags": #[#"one",#"two"],
#"number":#"4"
},
#{ #"tags": #[#"one",#"two",#"four"],
#"number":#"4"
},
#{ #"tags": #[#"chacho",#"chocho"],
#"number":#"4"
},
#{ #"tags": #[#"one"],
#"number":#"4"
} ];
NSArray* str = #[#"one",#"two"];
NSPredicate* pre = [NSPredicate predicateWithFormat:#"tags CONTAINS %# ",str];
myAr = [myAr filteredArrayUsingPredicate:pre];
NSLog(#"%#",myAr);
If I understand your question correctly, you just have to replace "CONTAINS" by "="
in the predicate:
[NSPredicate predicateWithFormat:#"tags = %# ",str]
This gives an array with all dictionaries where the "tags" value is equal to the
given array str. In your example, it returns an array with the second dictionary
only.
UPDATE: To find all dictionaries where the "tags" value is an array with the
given elements, but independent of the order, the following slightly more
complicated predicate should work:
[NSPredicate predicateWithFormat:#"tags.#count = %d AND SUBQUERY(tags, $t, $t in %#).#count = %d",
[str count], str, [str count]];
UPDATE 2: That was too complicated, the following predicate seems to work as well:
[NSPredicate predicateWithFormat:#"tags.#count = %d AND ALL tags in %#",
[str count], str]
(I have assumed that str contains only different elements.)
For an answer that uses neither a for loop nor predicate format strings, try using a block and make use of NSSet to determine if the set of tags you want to match is equal to a set of the array element's tags. For example:
NSSet* desiredTags = [NSSet setWithObjects:#"one", #"two", nil];
NSPredicate *tagFilterPredicate = [NSPredicate
predicateWithBlock:^BOOL (id data, NSDictionary *bindings) {
NSSet *tags = [NSSet setWithArray:[data objectForKey:#"tags"]];
return [desiredTags isEqual:tags];
}];
NSArray *resultArray = [myArr filteredArrayUsingPredicate:tagFilterPredicate];
Bear in mind that this does allocate a set per iteration. So, if you're looking to avoid allocations, this is not adequate. Otherwise, it at least avoids a format string.
A brute-force way to do this would be to remove your predicate and just enumerate:
NSArray *required = #[#"one", #"two"];
NSMutableArray *matches = [#[] mutableCopy];
[myAr enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSArray class]]) {
BOOL match = YES;
for (NSString *item in required) {
if (![obj containsObject:item]) {
match = NO;
break;
}
}
if (match && [(NSArray *)obj count] == required.count) {
[matches addObject:obj];
}
}
}];
}];
I have an NSArray which contains some NSString objects. For example:
NSArray *objects = #[#"Stin",#"Foo",#"Ray",#"Space"];
Now I need to sort this array based on following order of Strings.
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
So the answer should be
NSArray *sorted = #[#"Foo",#"Space",#"Ray",#"Stin"];
How can I achieve this?
ANSWER:
Based on Accepted answer of dasblinkenlight, I did following and it worked to charm.
NSMutableArray *objects = #[#"Star",#"Stin",#"Foo",#"Ray",#"Space",#"John"];
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
[objects sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int index1 = [sortOrder indexOfObject:obj1];
int index2 = [sortOrder indexOfObject:obj2];
if (index1 > index2)
return NSOrderedDescending;
if (index1 < index2)
return NSOrderedAscending;
return NSOrderedSame;
}];
Create NSComparator that holds a reference to the superset array, and decides the relative order of strings by comparing the results of calling [superset indexOfObject:str] on both strings. Call sortedArrayUsingComparator: passing an instance of NSComparator to get the desired ordering.
dasblinkenlight's solution would work, but like most programming problems there are multiple ways to go about it. Here is one such alternative:
NSMutableArray *sorted = [NSMutableArray arrayWithCapacity:0];
[sortOrder enumerateObjectsUsingBlock:^(NSString *sortedString, NSUInteger idx, BOOL *stop) {
if ([objects containsObject:sortedString]) {
[sorted addObject:sortedString];
}
}];
The variable names correspond to the variable names used in the original question.
This works because the enumeration happens in order. Therefore, what takes place is, essentially, a cull of the objects that exist in both arrays in the order as specified by sortOrder.