for(int i=0;i<[serviceNamesFilterArray count];i++){
NSLog(#"state : %#", [serviceNamesFilterArray objectAtIndex:i]);
NSString *str = [serviceNamesFilterArray objectAtIndex:i];
if (tag_id == [serviceNamesFilterArray indexOfObject:str] ) {
// filterButtonArray = serviceNamesFilterArray;
[filterButtonArray addObject:str];
NSLog(#"%#",filterButtonArray);
}
}
I want to access index of serviceNamesFilterArray. How can i access index's of my array so that i can compare it with integer tag_id?
Even Objective-C provides smarter filter APIs than a loop.
index will contain the index of the object in the array matching tag_id
NSInteger index = [self.serviceNamesFilterArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return (NSString *)obj.integerValue == tag_id;
}];
you can compare i value with your tag_id as follows:
for(int i=0;i<[serviceNamesFilterArray count];i++) {
NSLog(#"state : %#", [serviceNamesFilterArray objectAtIndex:i]);
NSString *str = [serviceNamesFilterArray objectAtIndex:i];
if (tag_id == i) {
//perform your logic here
}
}
you can use the enumerateObjectsUsingBlock method, like
[serviceNamesFilterArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// ...
}];
Preamble: you are using [array objectAtIndex:index] in your code, while this was the way to index historically in modern Objective-C you simply write array[index]. If you are learning Obj-C from a book/website you might want to look for a newer text.
It is unclear what you are asking let’s see what we can figure out.
You start with a loop:
for(int i=0;i<[serviceNamesFilterArray count];i++)
Here i is going to be used as an index into the array serviceNamesFilterArray. Inside the loop you then access the object at index i (updating your code as above):
NSString *str = serviceNamesFilterArray[i];
and having obtained the object at index i you ask what is the index of that object:
[serviceNamesFilterArray indexOfObject:str]
There are two possible answer here:
i – this is the most obvious answer and will be the result if there are no duplicates in serviceNamesFilterArray. It will be the answer as you just obtained str from index i of the array.
j where j < i – this will be the answer if the array contains duplicates and the same string is found at indices j and i. This result happens because indexOfObject: returns the first index at which the object occurs with the array.
The most likely result seems to be (1) in your case (guessing you do not have duplicate “service names”). In this case your conditional is equivalent to:
if (tag_id == i) {
[filterButtonArray addObject:str];
}
However if this is your intention then the loop is completely unnecessary as your code is equivalent to:
NSString *str = serviceNamesFilterArray[tag_id];
[filterButtonArray addObject:str];
If the serviceNamesFilterArray does contain duplicates then your code as written may add the string at index tag_id multiple times to filterButtonArray or it may add it no times – we'll leave figuring out why as an exercise, and we doubt this is your intention anyway.
At the time of writing #vadian has made a different guess as to your aim. Their solution finds the index, if any, where the string value if interpreted as an integer is equal to the value of tag_id (an integer). If that is your aim then #vadian’s solution provides it.
Of course both our and #vadian’s guesses might be wrong at to what your aim is. If so you can edit the question to explain, or delete it and ask a new one instead – given this question has at the time of writing 3 answers already deletion in this case might be better to reduce future confusion when people read the (revised) question and (outdated) answers.
HTH
Maybe you just need to judge whether the tag_id is less than count of serviceNamesFilterArray, then you can get the value by tag_id directlly.
if (tag_id < [serviceNamesFilterArray count]){
NSString *str = [serviceNamesFilterArray objectAtIndex:tag_id];
// other logic here
}
Related
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (condition) {
[array removeObject:obj];
}
}];
sometimes it worked,and sometimes it crashed,why?
Imagine how you would write the code for -enumerateObjectsUsingBlock: if you were working at Apple and asked to add this. It would probably look something like:
-(void) enumerateObjectsUsingBlock: (void(^)(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop))myBlock
{
NSUInteger numItems = [self count];
BOOL stop = false;
for( NSUInteger x = 0; x < numItems && stop == false; x++ )
{
myBlock( [self objectAtIndex: x], x, &stop );
}
}
Now if your block calls removeObject:, that means that the array:
0: A
1: B
2: C
3: D
After myBlock( A, 0 ) changes to:
0: B
1: C
2: D
Now x++ gets executed, so next call is myBlock( C, 1 ) -- already you see that the 'B' is now skipped, and the item originally at index 2 is deleted second (instead of the one at index 1). Once we have deleted that, we loop again and the array looks like this:
0: B
1: D
So when -enumerateObjectsUsingBlock: tries to delete the item at index 2, it runs off the end of the array, and you get a crash.
In short, the documentation for -enumerateObjectsUsingBlock: doesn't say anywhere that you may modify the array while you're iterating it, and there is no way for the block to tell the looping code in -enumerateObjectsUsingBlock: that it just deleted an object, so you can't rely on that working.
(You can try this out yourself ... rename this version of enumerateObjectsUsingBlock: to myEnumerateObjectsUsingBlock:, declare it in a category on NSArray, and step through it in the debugger with a program like: [myArray myEnumerateObjectsUsingBlock: ^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop){ [myArray removeObject: obj]; }];)
If you expect you'll be deleting items from the array, there are several workarounds. One, you can make a copy of the array, loop over that, and delete objects from the original array. Another option is to iterate backwards over the array, which means that the indexes of earlier items won't change (try modifying the version of -enumerateObjectsUsingBlock: above and watch what happens in the debugger).
Yet another approach is to write your own method that filters the array. You give it a block that is expected to return YES if you are to keep the object, NO if you shouldn't. Then you loop over the original array, call the block on each item, and create a new array, to which you add all objects for which the block returns YES.
This is unsafe way. I don't know well internal execution of this language, btw I think when you remove the object, then the size of array decreases, and it would be error when you reach the last element of array.
I think in enumeration block, you are not allowed to change array. it is same issue on other languages.
You can get further information in this url.
http://ronnqvi.st/modifying-while-enumerating-done-right/
After deletion, your objects are not proper. so make copy as:
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (condition) {
int counter = [array indexOfObject:obj];
[array removeObjectAtIndex:counter];
}
}];
I'm trying to perform the relatively simple task of deleting the items in an NSMutableArray that fit a certain criteria.
In this particular case, I'd like to delete the items where all of the following are true:
city: Seattle
state: WA
timestamp: more than 60 seconds old
An NSLog of my array looks like this:
array = (
{
city = "Seattle";
state = "WA";
timestamp = 1432844384;
},
{
city = "Dallas";
state ="TX";
timestamp = 1432844415;
},
{
city = "Seattle";
state = "WA";
timestamp = 1432845329;
}
)
I've tried using NSPredicate to filter the array, but I think that may be overcomplicating things. Any recommendations would be great! Thank you!
You could get the indexes of such elements by using indexesOfObjectsPassingTest: (see docs)
NSIndexSet *indexes = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
// check if obj is matching all your criteria
}
Then remove the items using removeObjectsAtIndexes: (see docs)
[array removeObjectsAtIndexes:indexes];
No, filtering out the items to delete and then deleting them is the right way. It iw not complicated, because obviously getting the specified items is filtering with a predicate + deleting them is deleting them. How could a solution be less complicated?
Maybe it is easier to you, to filter out the items not to delete. Simply add a NOT to your predicate.
Here your timestamp is unique every time. So you have delete data base on timestamp.
First you have to get index of that timestamp from that array.
NSInteger index = [[array valueForKey:#"timestamp"] indexOfObject:timestamp];
After getting this index of array you have to delete data from that index.
[array removeObjectAtIndex:index];
I have an NSArray filled with only NSStrings
I understand that to iterate over a NSArray of n elements, all I have to do is use for (NSString *element in arrayOfElements). However, I was wondering if there is specific function that will perform a comparison between every string element in the array with each other. For example, if I have the array:
[#"apple", #"banana", #"peach", #"kiwi"],
how would I do the comparison so apple is compared to banana, peach and then kiwi; and then banana is against peach and wiki, and finally peach is against kiwi?
Try using nested for loops, ex:
for (int i = 0 ; i < array.count ; i ++) {
for (int j = i + 1 ; j < array.count ; j ++) {
// compare array[i] to array [j]
}
}
Edit: And although wottle's suggestion would work, I'd recommend mine in this case, since it won't waste iterations going over the same comparisons multiple times. What I've done in this algorithm by setting j = i + 1 is compare each element in the array only to the ones after it.
Given "the array will not have any duplicates, and every NSString will be unique" this sounds like a great case for using NSSet classes instead of NSArray. NSMutableSet provides:
minusSet:
Removes each object in another given set from the receiving
set, if present.
and
intersectSet:
Removes from the receiving set each object that isn’t a
member of another given set.
I'm not sure which operation you're looking for but it sounds like one of those should cover your exact use case.
What you're trying to do is a bit beyond what custom comparators were meant to do. Typically when you have a list and you want to run a custom comparator, you're doing it to sort the list. You seem to want to do some specific action when certain items in the list compare to others, and for that, I think a loop within a loop is your best bet. It won't be very good performance, so hopefully you are not expecting a large array:
-(void) compareArrayToSelf
{
NSArray *array=#[#"apple", #"bananna", #"peach", #"kiwi"];
for( NSString *value1 in array)
{
for( NSString *value2 in array)
{
if( ![value1 isEqualToString:value2] && [self compareArrayValue:value1 toOtherValue:value2])
{
//Do something with either value1 or value2
}
}
}
}
I've finally pinpointed the exact issue to a problem I've been having, but I'm not sure how to fix it. Here is the general gist of what is happening: I have an array called wordArray, and I am iterating through it. This array can contain strings that are identical to each other and different from one another. All I want to do is retrieve the index of the particular instance that I am currently iterating, so I the simplified version is this:
for(NSString* stringInWordArray in wordArray){
NSLog(#"String is: %# Index is: %d",stringInWordArray,[wordArray indexOfObject:stringInWordArray]);
}
This will obviously print out the String, and the index of the string, however, according to the apple documentation, the indexOfObject method "Returns the lowest index whose corresponding array value is equal to a given object." This is a problem for me because if I have an array that looks like this:
["fruit","fruit","panda","happy",nil];
Then this will print:
String is fruit Index is 0
String is fruit Index is 0
String is panda Index is 2
String is happy Index is 3
Where I want the indices to print 0,1,2,3. Any workarounds for this issue?
Use a "normal" loop:
for (NSUInteger i = 0; i < wordArray.count; i++) {
NSString *stringInWordArray = wordArray[i];
NSLog(#"String is: %# Index is: %u",stringInWordArray, i);
}
Or you can do:
NSUInteger i = 0;
for(NSString* stringInWordArray in wordArray) {
NSLog(#"String is: %# Index is: %u",stringInWordArray, i);
i++;
}
Or you can do:
[wordArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *stringInWordArray = obj;
NSLog(#"String is: %# Index is: %u",stringInWordArray, idx);
}];
You could use a NSDictionary with key = index and the value = your string:
NSDictionary *dict = #{#"0" : #"fruit", #"1" : #"friut", #"2" : #"panda", #"3" : #"happy"};
The keys are unique, so you know which object you use depending on the key.
I am making a program where I need to loop through an array with a list of letters. I want the letters to be shown on their specific label. I have therefore created an outlet of each (about 38) and given them the name "alif01", "alif02", etc.
for (int i = 0; i < [arabicLetters count]; i++) {
int num = i;
NSString *letterString = [arabicLetters objectAtIndex:i];
NSLog(#"alif0%d is %#", num, letterString);
alif0**[i]**.text = arabicLetters[i];
}
is it possible to use the index [i] instead of writing it all manually?
You should not have 38 IBOutlet properties for this. You should have an array (possibly an IBOutletCollection) so that you can loop over the array / index into the array.
While technically you can create a key name and use KVC valueForKey: (appending strings / string format), the array approach is a much better solution.
Indeed, as you already have a loop, you would be better served by creating the labels in the loop directly, then you know you have the correct number. This is particularly beneficial later, when you change the contents of arabicLetters (though that sounds like it isn't a concern in this particular case).
Try with below code:
for (int i = 0; i < [arabicLetters count]; i++) {
NSString *letterString = [arabicLetters objectAtIndex:i];
NSString *propertyName = [NSString stringWithFormat:#"alif0%d.text",i];
[self setValue:letterString forKeyPath:propertyName];
}