Objective-c replaceObjectAtIndex not working because of array in array structure - ios

i have an array and i get value with this codes:
NSString *status = [[Sweetresponse objectAtIndex:path.row] objectAtIndex:9];
i wanna change this value :
[[Sweetresponse objectAtIndex:path.row] replaceObjectAtIndex:9 withObject:#"liked"];
But its not working because this structure is array in array. how can i fix this problem ?

This is because the NSArray is immutable. You have to make it mutable.
NSMutableArray *mutableResponse = [Sweetresponse mutableCopy];
NSMutableArray *mutableResponseItems = [[mutableResponse objectAtIndex:path.row] mutableCopy];
// replace at the index
[mutableResponseItems replaceObjectAtIndex:9 withObject:#"liked"];
// create immutables and replace it with our new array
mutableResponse[path.row] = [mutableResponseItems copy];
// set `Sweetresponse` (assuming it is an NSArray)
Sweetresponse = [mutableResponse copy];
edit: I have no idea what Sweetresponse really is, here I'm assuming it's a NSArray
#trojanfoe has a point with parsing this response into custom objects would lead to cleaner code and also simpler object modification.

Related

orderedSetWithOrderedSet:range:copyItems: NSArray Equivalent

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]];

Add object to array within NSMutableDictionary loaded from NSUserDefaults

I have an array inside a NSMutableDictionary and i want to add objects to it. With my current approach I get an error saying that the array is immutable.
I think the problem lies when I´m saving the dictionary to NSUserDefaults. I´m retrieving the is it a NSDictionary but I am at the same time creating a new NSMutableDictionary with the contents.
However, the array seems to be immutable. How do I replace an array inside of a dictionary?
My dictionary looks like this:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
I am trying to add objects like this:
[[[mutDict objectForKey:#"firstKey"] objectForKey:#"theArray"] addObject:[NSNumber numberWithInt:5]];
I am able to add objects to the array inside mutDict before its saved to NSUserDefaults
The error message I get when I try to add to the array inside the dictionary after loading it from NSUserDefaults:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
Here's what the documentation for dictionaryForKey: says on NSUserDefaults:
Special Considerations
The returned dictionary and its contents are immutable, even if the values you >originally set were mutable.
So when you retrieve your dictionary from NSUserDefaults the dictionary itself and all of the collections inside it are immutable. You can make the top level dictionary mutable (which I assume you are doing), but that won't propagate down into the now immutable NSArrays which are values in the dictionary.
The only way to get around this is to go through the dictionary that's returned and replace the immutable NSArrays with their mutable counterparts. It might look something like this.
- (NSMutableDictionary *)deepMutableCopyOfDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
for (id key in [mutableDictionary allKeys]) {
id value = mutableDictionary[key];
if ([value isKindOfClass:[NSDictionary class]]) {
// If the value is a dictionary make it mutable and call recursively
mutableDictionary[key] = [self deepMutableCopyOfDictionary:dictionary[key]];
}
else if ([value isKindOfClass:[NSArray class]]) {
// If the value is an array, make it mutable
mutableDictionary[key] = [(NSArray *)value mutableCopy];
}
}
return mutableDictionary;
}
To be honest though it sounds like you're using NSUserDefaults for something a lot more complex then it is intended for. If you want to persist complex data structures then you should look into something like Core Data, or if that looks to be a bit overkill take a look at NSKeyedArchiver.
You can add object directly to the array:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
//Since Objective-C objects are always passed by reference (using pointers) you can add object to the array
[array addObject:[NSNumber numberWithInt:55]];
Swift example of adding object to array which is part of a dictionary.
let arr = [0] // note that initial array may be immutable
var dict = ["fK": ["a":arr]] // even if "arr" will be mutable, but "dict" immutable
dict["fK"]!["a"]!.append(3) // this will not work. "dict" must be mutable
println(dict) //[fK: [a: [0, 3]]]
Another approach
var arr = [0] // initial array must be mutable
var dict = ["fK": ["a":arr]] // in both cases dictionary must be mutable
arr.append(3)
let newArr = arr
dict["fK"]!["a"]! = newArr // because we change it's content
println(dict) //[fK: [a: [0, 3]]]

Managing arrays - mutating method sent to immutable object

I try to populate existing array with other array:
if (!self.photos){
self.photos = [responseDictionary valueForKeyPath:#"data"];
} else {
NSArray *newPhotosFromFeed = [responseDictionary valueForKeyPath:#"data"];
[self.photos addObjectsFromArray:newPhotosFromFeed];
}
Compiler throw me an error - mutating method sent to immutable object .
Photos declared like this:
#property (nonatomic) NSMutableArray *photos;
Method declaration (from Apple.developer) is following:
- (void)addObjectsFromArray:(NSArray *)otherArray
So, how am i violate the rules?
I add NON-mutable array NSArray *newPhotosFromFeed to MUTABLE array self.photos.
So why am i get that error??
If this line is called first:
self.photos = [responseDictionary valueForKeyPath:#"data"];
then you will be storing an immutable array in a mutable property. The compiler can't tell it's wrong and the runtime doesn't check so you don't know it's happening. Then, later, when you call:
[self.photos addObjectsFromArray:newPhotosFromFeed];
you get the crash.
So, you should always ensure that the object being set to photos is mutable, the easiest way it by taking the mutableCopy as that will work on both mutable and immutable source objects:
self.photos = [[responseDictionary valueForKeyPath:#"data"] mutableCopy];
Here's the fix :
self.photos = [responseDictionary valueForKeyPath:#"data"].mutableCopy;
and
NSArray *newPhotosFromFeed = [responseDictionary valueForKeyPath:#"data"].mutableCopy;
You're attempting to set an NSMutableArray to a NSArray. That doesn't work without using mutableCopy. Use mutableCopy to create a mutable copy of the NSArray and that object becomes an NSMutableArray.

Capitalized NSArray of Strings?

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];

Creating NSArray from NSMutable Array

At the moment I'm creating an array as per below:
NSArray *data = #[#[#20, #40, #20, #60, #40, #140, #80],];
However I'd like to be able to create the same array from data already in an NSMutableArray.
I've tried this but the library I'm passing the array to does not like it.
NSArray *data = [NSArray arrayWithArray:self.altData];
("altData" is an NSMutableArray)
Any ideas?
An NSMutbaleArray is a subclass of an NSArray. So you can just do NSArray *immutable = [myMutableArray copy] or if you know that you're not going to change the mutable array after a point then you can do NSArray *immutable = mutableArray; maybe followed by mutableArray = nil. Your solution is also fine.

Resources