As the screenshot showing, anyone who knows what's the problem of it? Thanks for kindly help!
In you for look just put codeList and your problem will be fixed.
check this I modified your method.
-(NSArray *) checkVisibleCode:(NSArray *) codeList{
NSMutableArray *list = [NSMutableArray arrayWithArray:codeList];
for (NSString *code in codeList) {
if (![self.codeMapping.allKeys containsObject:code]) {
[list removeObject:code];
}
}
return list;
}
You are removing object from the Array you're iterating on.
It's a bad practice that can lead to mistakes like this.
You simply have to replace
for (NSString *code in list)
with :
for (NSString *code in codeList)
Don't delete objets from an array or dictionary or any collection set while you're iterating on it .
Keep the indexes you want to delete when iterating then remove all the objets. You can use indexesOfObjectsPassingTest method :
NSMutableArray * list = [NSMutableArray arrayWithArray:codeList];
NSIndexSet * indexesToRemove = [list indexesOfObjectsPassingTest:^BOOL(NSString *code, NSUInteger idx, BOOL *stop) {
return [self.codeMapping.allKeys containsObject:code];
}];
[list removeObjectsAtIndexes:indexesToRemove];
You should do this using indexesOfObjectsPassingTest and objectsAtIndexes: Even if you follow for example Anbu's reply to make your code work, this code runs in O (n^2). Do it with a list of 100,000 objects where every second object gets removed, and your app dies a sad death because it runs out of CPU time.
Related
So we're converting some old code and I need to change
NSSet *set = [NSOrderedSet orderedSetWithOrderedSet:filteredSubcategories
range:[range rangeValue]
copyItems:NO];
into an array. Is there some kind of array equivalent to this? Can someone help me do this?
https://developer.apple.com/reference/foundation/nsorderedset/1543292-orderedsetwithorderedset
Please see below a solution, but please check the initial purpose of using Set instead of Array in the original code. The change that you are going to make may reduce performance if originally using set is:
To prevent duplicate or if there is a check if an object exist inside a loop.. Array and Set have the same method name contains: but a huge difference on performance.
for (NSNumber *number in myArray) {
if([myArrayOfCategories containsObject:#9]){ //O(n) worst look up
///do sthg
}
}
for (NSNumber *number in myArray) {
if([mySetOfCategories containsObject:#9]){ //O(1) fast lookup
///do sthg
}
}
//Solution
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)];
NSArray* content = [filteredSubcategories objectsAtIndexes:indexSet];
Either create set as in the OP and get its array representation, like this:
NSSet *set = [NSOrderedSet orderedSetWithOrderedSet:filteredSubcategories //... as in the OP
NSArray *array = [set array];
Or, if you don't need the set, just subrange filteredSubcategories array representation, like this:
NSArray *array = [[filteredSubcategories array] subarrayWithRange:[range rangeValue]];
Without unintentionally killing performance, does this appear at first glance to be acceptable for perhaps 200 guid strings in one list compared for equality with 100 guid strings from another list to find the matching indexes.
I have a method signature defined like so...
-(NSArray*)getItemsWithGuids:(NSArray*)guids
And I wanted to take that passed in array of guids and use it in conjunction with this array...
NSArray *allPossibleItems; // Has objects with a property named guid.
... to obtain the indexes of the items in allPossibleItems which have the matching guids from guids
My first instinct was to try indexesOfObjectsPassingTest but after putting together the block, I wondered whether the iOS framework already offers something for doing this type of compare more efficiently.
-(NSArray*)getItemsWithGuids:(NSArray*)guids
{
NSIndexSet *guidIndexes = [allPossibleItems indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
SomeObjWithGuidProperty *someObject = obj;
for (NSString *guid in guids) {
if ([someObject.guid isEqualToString:guid]) {
return YES;
}
}
return NO;
}];
if (guidIndexes) {
// Have more fun here.
}
}
Since you're working with Objective-C (not Swift) check out YoloKit. In your case, you can do something like:
guids.find(^(NSString *guid){
return [someObject.guid isEqualToString:guid];
});
My thought would be to use a set -
-(NSArray*)getItemsWithGuids:(NSArray*)guids inAllObjects:(NSArray *)allObjects
{
NSSet *matchGuids=[NSSet setWithArray:guids];
NSMutableArray *matchingObjects=[NSMutableArray new];
for (SOmeObjectWithGuidProperty *someObject in allObjects) {
if ([matchGuids contains:someObject.guid]) {
[matchingObjects addObject:someObject];
}
}
return [matchingObjects copy];
}
Your code looks like it would have O(n^2) performance, which is bad. I think the solution of converting guids to an NSSet and then using NSSet's containsObject would likely be much more performant. You could rewrite your indexesOfObjectsPassingTest code to use an NSSet and containsObject pretty easily.
If order doesn't matter much, I would suggest to change data structure here. Instead of using NSArray, consider to use NSDictionary with guid as key and someObject as value. In this case, you should use -[NSDictionary objectsForKeys:notFoundMarker:] method to obtain objects.
It will work much faster, than enumeration trough 2 arrays. If the NSDictionary key have a good hash function, accessing an element, setting an element, and removing an element all take constant time. NSString has good hash.
-(NSArray*)getItemsWithGuids:(NSArray*)guids {
NSArray *objectsAndNulls = [allPossibleItemsDictionary objectsForKeys:guids notFoundMarker:[NSNull null]];
if (objectsAndNulls) {
// Have more fun here.
// You should check that object in objectsAndNulls is not NSNull before using it
}
return objectsAndNulls;
}
UPD Unfortunately, there is no way to pass nil as notFoundMarker. If you can't provide usable notFoundMarker value and don't want to perform additional checks, you can query objects one by one and fill NSMutableArray. In this case you will avoid pass trough array to remove NSNulls:
-(NSArray*)getItemsWithGuids:(NSArray*)guids {
NSMutableArray *objects = [NSMutableArray arrayWithCapacity:guids.count];
for (NSString *guid in guids) {
SomeObjWithGuidProperty *object = allPossibleItemsDictionary[guid];
if (nil != object) {
[objects addObject:object];
}
}
if (nil != objects) {
// Have more fun here.
}
return object;
}
I have an NSarray called array. And it look like this
array = #[#"one", #"two", #"three"];
I want this array to be capitalized. What is the best way to go about this. I can only think of making an NSMutableArray called mutableArray.
And do something like this
for(int i = 0; i < array.lenght; i++) {
self.mutableArray = addObject:[array[i] capitalizedString];
}
Or is there another better way?
The magic method you are looking for does in fact exist.
NSArray *array = #[#"one", #"two", #"three"];
NSArray *capArray = [array valueForKeyPath:#"capitalizedString"];
SWIFT
You Can use map
let array = ["one", "two", "three"]
let upercaseArray = array.map({$0.uppercased()})
now you have upercaseArray like ["ONE","TWO","THREE""]
What you really want is a sort of transform method, which takes an array and a selector, then returns an array of the results of performing that selector on each object. Unfortunately that doesn't exist in vanilla objective-C.
Your approach is generally fine, but I would be careful of two points. Firstly, make sure you create the NSMutableArray with the capacity of the NSArray you are copying, as this will avoid any reallocation overhead as you add objects to it. Secondly, you might want to copy the mutable array so you end up with an immutable NSArray as the final result.
So I would use something like this:
- (NSArray *)capitalizeStringArray:(NSArray *)array {
// Initialize tempArray with size of array
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSString *str in array) {
[tempArray addObject:[str capitalizedString]];
}
return [tempArray copy]; // convert back to NSArray]
}
You can convert this to a category method on NSArray if you like, and generalize it to use other selectors if you wish.
There's about a gazillion ways to handle this. For small arrays, pick whichever you find easier to understand.
I'd probably use code like this:
- (NSMutableArray *) capitalizedArrayFromArrayOfStrings: (NSArray*) array;
{
NSMutableArray *result = [NSMutableArray arrayWithCapacity: array.count];
for (NSString *string in array)
{
if ([string isKindOfClass: [NSString class]]
[result addObject: [string capitalizedString];
}
}
Creating your array with the correct capacity at the beginning enables the array to allocate enough space for all it's future elements and saves it having to allocate more space later.
Using for..in fast enumeration syntax is more efficient than using array indexing, but for short arrays the difference is small. The code is also simpler to write and simpler to read, so I prefer that syntax where possible.
As Alex says, you could also create a category method on NSArray that would return a capitalized version of your array, or even a category on NSMutableArray that would replace the strings in the array "in place".
Works like charm.
NSString *myString = YOUR_ARRAY.uppercaseString;
[myNSMutableArray addObject:myString];
I have an NSArray that has to be an array for portions of the project so nothing will change it. I need to add an object to the array. The method I used was to convert to an NSMutableArray, add the object, and then convert back to an NSArray. The method:
- (void)addAdj:(NSString *)obj{
NSMutableArray *ary = [self.adj mutableCopy];
[ary addObject:obj];
self.adj=[ary copy];
for(int i = 0; i<[self.adj count]; i++){
NSLog(#"%#, ", [self.adj objectAtIndex:i]);
}
}
The for loop and log statement are included to print the array but it does not print anything at all. I have seen similar questions but people always tell the OP to just use an NSMutable array from the start. I'd like to know why this bit of code does not work as is. In advance Thanks!
May be you have already forgot to initialize self.adj in viewDidload. You try to add below code to viewDidload:
self.adj = [[NSArray alloc]init];
Or you can show code in your viewDidload. I will be more help.
How can I remove an object from a reversed NSArray.
Currently I have a NSMutableArray, then I reverse it with
NSArray* reversedCalEvents = [[calEvents reverseObjectEnumerator] allObjects];
now I need to remove at item from reversedCalEvents or calEvents and automatically refresh the table the array is displayed in based on conditions.
i.e.
if(someInt == someOtherInt){
remove object at index 0
}
How can I do this? I cannot get it to work.
Here's a more functional approach using Key-Value Coding:
#implementation NSArray (Additions)
- (instancetype)arrayByRemovingObject:(id)object {
return [self filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != %#", object]];
}
#end
You will need a mutable array in order to remove an object. Try creating reversedCalEvents with mutableCopy.
NSMutableArray *reversedCalEvents = [[calEvents reverseObjectEnumerator] allObjects] mutableCopy];
if (someInt == someOtherInt)
{
[reversedCalEvents removeObject:object];
}
NSArray is not editable, so that you cannot modify it. You can copy that array to NSMutableArray and remove objects from it. And finally reassign the values of the NSMutableArray to your NSArray.
From here you will get a better idea...
NSArray + remove item from array
First you should read up on the NSMutableArray class itself to familiarize yourself with it.
Second, this question should show you an easy way to remove the objects from your NSMutableArray instance.
Third, you can cause the UITableView to refresh by sending it the reloadData message.
you can try this:-
NSMutableArray* reversedCalEvents = [[[calEvents reverseObjectEnumerator] allObjects] mutableCopy];
[reversedCalEvents removeLastObject];