Check if NSArray contains object with specific property - ios

I have an array of UIView. I want to check if that array contains UIView with specific tag. If it does then I should get that view or else I should receive nil.
As of now I using following
// validCells is an array UIView
NSPredicate *p = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *ignored){
return ((UIView *)obj).tag == i;
}];
UIView *cell = [[validCells filteredArrayUsingPredicate:p] lastObject];
This works fine but complexity is n^2. I was wondering if there is any other better way to do it.
Thanks.

I don't think the complexity of your method is O(n^2), it is more probably like O(n).
But there is no reason to create a temporary array if you just search for a specific
element. As #Josh said, you can do a simple enumeration.
If you want to be a bit more fancy, you can write it as
NSUInteger index = [validCells indexOfObjectPassingTest:^BOOL(UIView *view, NSUInteger idx, BOOL *stop) {
return view.tag == idx;
}];
if (index != NSNotFound) {
cell = validCells[index];
}

Related

Find index of value which is stored into NSDictionary and NSDictionary stored into NSMutableArray

I have NSMutableArray which stores NSDictionary. Consider following array which contain NSDictionary.
<__NSArrayM 0x7f9614847e60>(
{
"PARAMETER_KEY" = 1;
"PARAMETER_VALUE" = ALL;
},
{
"PARAMETER_KEY" = 2;
"PARAMETER_VALUE" = ABC;
},
{
"PARAMETER_KEY" = 3;
"PARAMETER_VALUE" = DEF;
},
{
"PARAMETER_KEY" = 4;
"PARAMETER_VALUE" = GHI;
},
{
"PARAMETER_KEY" = 5;
"PARAMETER_VALUE" = JKL;
}
)
I can find index of specific NSDictionary using following code.
int tag = (int)[listArray indexOfObject:dictionary];
But If I have PARAMETER_VALUE = GHI and using this value I want to find that dictionary index into array. I don't want to use for loop. Can I get index without for loop?
You can use indexOfObjectPassingTest method of NSArray:
[listArray indexOfObjectPassingTest:^BOOL(NSDictionary* _Nonnull dic, NSUInteger idx, BOOL * _Nonnull stop) {
return [dic[#"PARAMETER_VALUE"] isEqualToString:#"GHI"];
}];
Also, please consider using indexesOfObjectsPassingTest if you can have multiple dictionaries with the same PARAMETER_VALUE
You can add a category on NSArray like this (this does a type safety check as well; only array of dictionaries are processed):
- (NSInteger)indexOfDictionaryWithKey:(NSString *)iKey andValue:(id)iValue {
NSUInteger index = [self indexOfObjectPassingTest:^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
if (![dict isKindOfClass:[NSDictionary class]]) {
*stop = YES;
return false;
}
return [dict[iKey] isEqual:iValue];
}];
return index;
}
And then simply call indexOfDictionaryWithKey:andValue: directly on your array object to get the index.
Just in case if you want to get the dictionary object out of that array, add one more category in NSArray:
- (NSDictionary *)dictionaryWithKey:(NSString *)iKey andValue:(id)iValue {
NSUInteger index = [self indexOfDictionaryWithKey:iKey andValue:iValue];
return (index == NSNotFound) ? nil : self[index];
}
You can use NSPredicate for this purpose:
// Creating predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.PARAMETER_VALUE MATCHES %#",#"GHI"];
// Filtering array
NSArray *filteredArr = [arr filteredArrayUsingPredicate:predicate];
// If filtered array count is greater than zero (that means specified object is available in the array), checking the index of object
// There can be multiple objects available in the filtered array based on the value it holds (In this sample code, only checking the index of first object
if ([filteredArr count])
{
NSLog(#"Index %d",[arr indexOfObject:filteredArr[0]]);
}
Well, one has to enumerate in a way. Taking your requirement literally (no for loop), you can use fast enumeration. However, the task can be run concurrently, because you only need read access:
__block NSUInteger index;
[array enumerateObjectsWithOptions: NSEnumerationConcurrent
usingBlock:
^(NSDictionary *obj, NSUInteger idx, BOOL *stop)
{
if( [obj valueForKey:#"PARAMETER_VALUE" isEqualToString:#"GHI" )
{
index = idx;
*stop=YES;
}
}

Get index of last repeated string in NSMutableArray

I have a NSMutableArray with multiple repeated strings, I am trying to get last string index.
NSMutableArray *arrWithRepeatedStrings=[[NSMutableArray alloc]initWithObjects:#"iOS",#"apple",#"iOS",#"apple",#"apple",#"iOS", nil];
here iOS and apple are the repeated strings in my array. I think it's possible and am on the way to it. Can anyone help me.
You can get the index of last occurrence of string by enumerating in the reverse direction. Use the code below. You can change the matching string to #"iOS" if you want it's index and not #"apple".
NSMutableArray *arrWithRepeatedStrings=[[NSMutableArray alloc]initWithObjects:#"iOS",#"apple",#"iOS",#"apple",#"apple",#"iOS", nil];
NSInteger index = [arrWithRepeatedStrings indexOfObjectWithOptions:NSEnumerationReverse passingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if([obj isEqualToString: #"apple"])
return YES;
else
return NO;
}];
I understand the question that you want the index of the last object that is more than once in the array. This is quite different from what #Gandalf's solution does, so here's my take:
NSInteger index = [array indexOfObjectWithOptions:NSEnumerationReverse
passingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
BOOL match = [arrWithRepeatedStrings indexOfObject:obj] != idx;
if (match) *stop = YES;
return match;
}];

How to check the duplicate NSObjects in NSMutableArray

I receive data for an object person in sets of 5. Let's say name,age,gender,email,number. I did following to add the strings to NSobject:
DataObject *data=[DataObject new];
data.name=#"name";
data.age=#"age";
data.email=#"email";
//here i want to check for duplicates
[personArray addObject:data];
However, I want to check if the personArray is having the duplicate NSObjects or not.
I tried this,but it didnt work:
if(![personArray containsObject:data]){
//add data
}
Edit: Actually, this is what I am trying to do:
I am getting the JSON repsonse and I am adding the properties into array. Before I used to get only one property,in that case, I did the following to eliminate the duplicates:
[JSON[#"person"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![obj[#"email"] isEqual:[NSNull null]] && ![personArray containsObject:obj[#"email"]] ) {
[personArray addObject:obj[#"email"]];
}
}];
Later I got 5 properties for person, so I thought instead of adding them all to the array, I used NSObject class to tie the properties together and add one person to the array.
[JSON[#"person"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![obj[#"email"] isEqual:[NSNull null]] && ![personArray containsObject:obj[#"email"]] ) { //how to check for the duplicates here?
DataObject *data=[DataObject new];
data.name=#"name";
data.age=#"age";
data.email=#"email";
[personArray addObject:data];
}
}];
If you do this:
DataObject *data = [DataObject new];
You have just created a new instance of data. No other object inside the personArray can be equal to that new instance.
I assume you're actually trying to check to see if there is a data object that contains the same properties as other data objects in the personArray. There's a number of ways you could do this (I like Zaph's answer, it's clean), but for simplicity...
DataObject *data=[DataObject new];
data.name=#"name";
data.age=#"age";
data.email=#"email";
BOOL contains = NO;
for (DataObject *object in personArray) {
if ([object.name isEqualToString:data.name] && [object.age isEqualToString:data.age] && [object.email isEqualToString:data.email]) {
contains = YES;
break;
}
}
if (!contains) {
[personArray addObject:data];
}
You need to implements isEqual for the DataObject class. Then [personArray containsObject:data] should work.
For details see:
Equality by Mattt Thompson.
Implementing Equality and Hashing by Mike Ash

idiomatic way to enumerate NSArray by both index and element

I need to do something similar to python's enumerate() function with an NSArray in iOS (I have to build NSIndexPath objects as well as examine the object).
I don't see a built in method for doing something like this (i.e. no NSArray equivalent of NSDictionary's enumerateKeysAndObjectsUsingBlock: method). Which leaves me with two general approaches I can think of.
for (NSUInteger index = 0; index < mySequence.count; index++) {
MyElementType *element = mySequence[index];
//
// code that works with both index and element
//
}
or
NSUInteger index = 0;
for (MyElementType *element in mySequence) {
//
// code that works with both index and element
//
index++;
}
Is there a good reason to prefer on or the other? Or is there a third approach that is better than either of these?
There is following API present in NSArray:
- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))

Need assistance regarding NSPredicate

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

Resources