In swift i am using this code to get particular object for a particular value
if let layer = self.layers.first(where: {$0.id == id}) { }
I want to use this same in objective-c. How should i get a object from array of objects for particular value
You can use NSPredicate in Objective-C to filter an array.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
id layer = [[self.layers filteredArrayUsingPredicate:predicate] firstObject]
The predicateWithFormat solution is short, but not as type-safe as Swift.
To make it a bit more type-safe you could use indexOfObjectPassingTest.
Assuming you have:
#interface MyLayer
#property int layerID;
#end
NSArray<MyLayer *> *layers = #[...];
int layerIDToFind = 123;
You can write:
NSUInteger index = [layers indexOfObjectPassingTest:^BOOL(MyLayer *layer, NSUInteger idx, BOOL *stop) {
return layer.layerID == layerIDToFind;
}];
if (index != NSNotFound) {
MyLayer *layer = layers[index];
// ... act on the layer ...
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", id];
NSArray *result = [self.layers filteredArrayUsingPredicate:predicate];
if result.count > 0
{
id layer = [result firstObject].id
}
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"]];
My NSMutableArray contains the following data
<DoctorInfo: 0x15de99e0> (entity: DoctorInfo; id: 0x15defbe0 <x-coredata://39D9B/DoctorInfo/p8> ; data: {
doctorName = nil;
emailAdd = nil;
hospitalName = nil;
mobileNumber = nil;
phoneNumber = nil;
}),
<DoctorInfo: 0x15de9b00> (entity: DoctorInfo; id: 0x15da5dc0 <x-coredata://39D9BED3/DoctorInfo/p10> ; data: {
doctorName = nil;
emailAdd = nil;
hospitalName = nil;
mobileNumber = nil;
phoneNumber = nil;
})
)
How can I remove those objects with nil? I have tried the following code by changing NSMutableArray to the NSArray and then filter it but it is still not working:
NSString *predString = [NSString stringWithFormat:#"(doctorName BEGINSWITH[cd] '%#')", nil];
NSPredicate *pred = [NSPredicate predicateWithFormat:predString];
self.filteredDocInfoArray = [self.unfilteredDocInfoArray filteredArrayUsingPredicate:pred];
You test for nil in predicate strings like this:
NSPredicate *pred = [NSPredicate predicateWithFormat: #"doctorName = nil"];
See the documentation for examples.
You cannot add a nil object to array. In your case you are adding an object that has some nil properties!
You can try this:
[yourArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
DoctorInfo *customObj = obj;
if (customObj.doctorName) {
//add obj to temp array
}
}];
Now you can replace yourArr with tempArr
If you want to use NSPredicate, please refer this.
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 know I can check if a string contains another string like this
NSString *string = #"hello bla bla";
if ([string rangeOfString:#"bla"].location == NSNotFound) {
NSLog(#"string does not contain bla");
} else {
NSLog(#"string contains bla!");
}
But what if I have an NSArray *arary = #[#"one",#"two", #"three", #"four"] and I wanted to check if a string contains either one of these without just loop or have a bunch of or's (|| ). So it would be something like this
if (array contains one or two or three or four) {
//do something
}
But if I have a longer array this becomes tedious so is there another way, without just looping through?
EDIT
I want to check if myArray has any of theses values in valuesArray
valuesArray =#[#"one",#"two", #"three", #"four"];
myArray = [#"I have one head", #"I have two feet", #"I have five fingers"]
OUTPUT
outputArray = #[#"I have one head", #"I have two feet"]
There you go:
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id __nonnull evaluatedObject, NSDictionary<NSString *,id> * __nullable bindings) {
for(NSString* val in valuesArray) {
if ([evaluatedObject rangeOfString:val].location != NSNotFound)
return true;
}
return false;
}]];
arrRet contains exactly the two desired strings.
A little bit more magic later you have your code without writing a loop :P
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *,id> * bindings) {
BOOL __block match = false;
[valuesArray enumerateObjectsUsingBlock:^(id __nonnull obj, NSUInteger idx, BOOL * __nonnull stop) {
*stop = match = [evaluatedObject rangeOfString:obj].location != NSNotFound;
}];
return match;
}]];
You could use a NSCompoundPredicate
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Where your subPredicates must look like
(
SELF CONTAINS[c] "one",
SELF CONTAINS[c] "two",
SELF CONTAINS[c] "three",
SELF CONTAINS[c] "four"
)
To get there from
NSArray *array = #[#"one", #"two", #"three", #"four"]
You could use a for loop, but as you are opposed to that, let's cheat:
by using a category I each NSArray functional mapping, but instead of looping, I use enumerating
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
Now I can create the subPredicates like
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
and create the compound predicate like
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
and use it
BOOL doesContain = [predicate evaluateWithObject:string];
et voilà: No (obvious) looping, while there is one hidden in the enumeration and probably in the predicate as-well.
Now with the changed question you basically ask for filtering. You can use the same predicate for that:
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
results now contains
(
I have one head,
I have two feet
)
the complete code
#import <Foundation/Foundation.h>
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
}
return 0;
}
Besides my cheating the my other question, here an idea how really to avoid time costly looping: Use Set computation magic!
created a class 'Sentence', instantiate it with the strings to test
created a 'Word' class that takes a word to search for
overwrite both classes' isEqual: method to match for if a word is in the sentence (use sets there too!)
put those into an array.
from this array create a NS(*)Set object
put all word in a set
execute union on it.
I am currently working on a app where i want to check if the particular object exists in the NSDictionary or not. I have tried this code but it does not seem to work. Any help will be appreciated!
for (int i=0; sizeofarray; i++) {
if ([[self.chat valueForKeyPath:#"text"] count] > 0)
{
NSDictionary* chatmessage=[self.chat objectAtIndex:i];
if ([chatmessage[#"text"] isEqualToString:#"Guest787" ]) {
NSLog(#"this happened");
}}
}
P.S sizeofarray is the length of the array and chat is the array which is stored in the dictionary.
For this task – find a dictionary with a specific key/value pair in an array – a predicate is a suitable solution.
NSString *value = #"Guest787";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"text == %#", value];
NSArray *result = [self.chat filteredArrayUsingPredicate:predicate];
BOOL valueExists = result.count > 0;
NSLog(#"%d", valueExists);
Or with Key-Value Coding (KVC)
BOOL valueExists = [[self.chat valueForKey:#"text"] containsObject:#"Guest787"];
Maybe this ?
if ([self.chat containsObject:#"Guest787"]) {
NSLog(#"this happened");
}
OR
for (NSString *message in self.chat) {
if ([message isEqualToString:#"Guest787"]) {
NSLog(#"this happened");
}
}