Init array from another without modifying its content - ios

I need to make a "clone" of an array to another, but the thing is that when I modify my copied Array, the original is modified too. Using hard copy is not working as I expect.
I'm initializing an array like this:
NSMutableArray *otherArray = [[NSMutableArray alloc] initWithArray: myList copyItems:YES];
where myList is a NSArray that came as a parameter in my function.
The thing is when I need to return myList, it's content has been modified when I modify my otherArray
I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them.
Also I tried with this:
NSMutableArray *otherArray = [myList mutableCopy];
Well, the obvious question is: How can I modify a copied object without modifying the original one?
Thanks!
EDIT: Here is my entire function.
RAC(self,filteredPacks) = [RACSignal combineLatest:#[self.searchBoxSignal, self.packListsSignal]
reduce:^NSArray *(NSString *filterString, NSArray *packList) {
NSMutableArray *sweetHelper = [[NSMutableArray alloc] init];
NSMutableArray* packListCopy = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:packList]];
filterString = [filterString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([filterString length] > 0)
{
for(PackList *theList in packListCopy){
NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet];
NSUInteger currentIndex = 0;
for(Pack *thePack in theList.resolved_packs){
if([thePack.name rangeOfString:filterString options:NSCaseInsensitiveSearch].location == NSNotFound){
[indexesToDelete addIndex:currentIndex];
}
currentIndex++;
}
[theList.resolved_packs removeObjectsAtIndexes:indexesToDelete];
[theList.packs removeObjectsAtIndexes:indexesToDelete];
[sweetHelper addObject:theList];
}
return sweetHelper;
}
else
{
return self.originalList;
}
}
];

Well, I found the solution using this answer and this comment in the same question.
As I said in my question:
"I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them."
Well, the properties that were not converted, they weren't because they are custom objects, An I didn't implemented the initWithCode and encodeWithCoder methods. So, when I added those methods to my customObjectClass, my Arrays were hard copied with all their elements.
A little example about what I did:
In my SomeCustomObject.h I should implements NSCoding:
#interface SomeCustomObject : NSObject <NSCoding> {
NSMutableArray * __packs;
int __type;
Link * __selfRef;
NSMutableArray * __resolved_packs;
}
And in my subclass of SomeCustomObject I got something like:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
__packs = [decoder decodeObjectForKey:#"packs"];
__type = [decoder decodeIntForKey:#"type"];
__selfRef = [decoder decodeObjectForKey:#"selfRef"];
__resolved_packs = [decoder decodeObjectForKey:#"resolved_packs"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:__packs forKey:#"packs"];
[encoder encodeInt:__type forKey:#"type"];
[encoder encodeObject:__selfRef forKey:#"selfRef"];
[encoder encodeObject:__resolved_packs forKey:#"resolved_packs"];
}
I hope this be useful to somebody :)

Related

Encoding, decoding, and editing a NSMutableArray with NSCoding

I followed along with Ray Wenderlich's tutorial for saving game data using a singleton and NSCoding (http://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2). Everything they did I've been able to use for NSStrings (changing value, storing, using in my project). I tried to make an NSMutableArray so I could add to and save a list of strings, though, and it didn't work. There is probably a very simple solution to this, so thanks in advance for putting up with a newbie.
My code:
in RWGameData.h (my singleton for using game data):
#property (nonatomic, strong) NSMutableArray *dataArray;
in RWGameData.m, after #implementation:
static NSString* const GameDataArrayofValues = #"dataArray";
-(void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.dataArray forKey:GameDataArrayofValues];
}
-(instancetype)initWithCoder:(NSCoder *)decoder {
self = [self init];
if (self) {
_dataArray = [[decoder decodeObjectForKey:GameDataArrayofValues] mutableCopy];
}
return self;
}
(there's also all the other code from that tutorial, like a save method etc.)
in myGame.m
[[RWGameData sharedGameData].dataArray addObject:#"objectString"];
[[RWGameData sharedGameData] save];
Logging the contents of the array right after this shows it as empty. [RWGameData sharedGameData].dataArray accepts the addObject call because I created it as an NSMutableArray, but it seems like it isn't actually going through.
Any ideas?
My guess is _dataArray is nil.
You should add code to your initWithCoder that checks to see if it was able to read array data. If not, _dataArray will be nil. In that case you should initialize an empty mutable array.
Your updated initWithCoder might look like this:
-(instancetype)initWithCoder:(NSCoder *)decoder {
self = [self init];
if (self)
{
_dataArray = [[decoder decodeObjectForKey:GameDataArrayofValues] mutableCopy];
if (_dataArray)
_dataArray = [[NSMutableArray alloc] init];
}
return self;
}
Likewise you need to implement a "regular" init method that sets _dataArray to an empty mutable array.

How can I automatically initialize mutable arrays/ dictionaries for my PFObject subclass? (Parse.com framework)

I have the following code in various parts of my app:
MyPFObjectSubclass *instance = [MyPFObjectSubclass object];
instance.myMutableArray = [NSMutableArray array];
instance.myMutableDictionary = [NSMutableDictionary array];
I am constantly forgetting to initialize these types, and running into problems later when I attempt setValue:forKey or addObject methods.
It's a nice-to-have, I admit, but I would like to play it safe and roll these initializations into +object if possible.
My PFObject subclasses all look roughly like this. I call [MyPFObject tlObject] to create a new instance.
#implementation MyPFObject
#dynamic objectUUID;
// Other fields
+ (MyPFObject*) tlObject
{
MyPFObject* obj = [self object];
[obj tlObjectInit];
// Other initializations
return obj;
}
+ (NSString*) parseClassName
{
return #"MyPFObject";
}
+ (PFQuery*) query
{
PFQuery* query = [PFQuery queryWithClassName: [self parseClassName]];
// Add includeKeys here
return query;
}
#end
I have a category on PFObject that includes tlObjectInit. The field objectUUID is there so that I have a value that can uniquely identify the object BEFORE IT IS SAVED. This is necessary because I sometimes create sets of objects that refer to one another. The Parse objectID is not set until it is saved.
#implementation PFObject (TL)
- (void) tlObjectInit
{
NSString* format = [[self parseClassName] stringByAppendingString: #"-%#"];
[self setObject: [NSUUID uuidStringInFormat: format]
forKey: #"objectUUID"];
}
// Add other initializations here
#end
In your MyPFObjectSubclass, override the init method:
-(instancetype) init
{
if (self = [super init])
{
_myMutableArray = [NSMutableArray array];
_myMutableDictionary = [NSMutableDictionary dictionary];
}
return self;
}
I guess your object method call one way or the other the init method.
Edit:
It looks like you use the Parse framework. As told in the reference, the PFObject init method shouldn't be overridden by subclasses.

Hash Table Code Explanation

I found this implementation of a hash table written in objective-c. I can follow almost all of it, but am struggling to understand how exactly the -(id) init function works. This is the method in the HashTable.m file with 3 lines (I repasted it below right after the question). Could someone explain what exactly it is doing? I included some of the other relevant code although for the most part I think I can follow the rest. Despite that I'm unclear as to the specifics of the init method. Thanks
-(id)init
{
self =[super init];
self.othercontainer = [[NSMutableArray alloc]init];
return self;
}
HashTable.h
#import <Foundation/Foundation.h>
#interface HashTable : NSObject
#property(nonatomic) NSMutableArray* othercontainer;
-(id)objectForKey:(NSString*)name;
-(void)setObject:(id)object forKey:(NSString*)name;
-(id)init;
#end
HashTable.m
#import "HashTable.h"
#import "Person.h"
#implementation HashTable
-(id)init
{
self =[super init];
self.othercontainer = [[NSMutableArray alloc]init];
return self;
}
-(id)objectForKey:(NSString*)name
{
Person* tempPerson = nil;
for (id item in self.othercontainer)
{
NSString* tempName = [((Person*)item) name];
if ([tempName isEqualToString:name])
{
tempPerson = item;
break;
}
}
return tempPerson;
}
-(void)setObject:(id)object forKey:(NSString*)name
{
[self.othercontainer addObject:object];
}
#end
Part of ViewController.m
NSData *data;
NSFileHandle *fh;
NSString *inBoundFile = #"/Users/user/Desktop/names.txt";
NSString *fileString;
fh = [NSFileHandle fileHandleForReadingAtPath:inBoundFile];
data = [fh readDataToEndOfFile];
fileString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSArray *PersonArray = [fileString componentsSeparatedByString:#"\n"];
self.container = [[HashTable alloc]init];
for (int x= 0; PersonArray.count > x ;x++) {
NSArray* tempNameandAddress = [PersonArray[x] componentsSeparatedByString:#" "];
Person *personA = [[Person alloc]init]; //could be other ways of defining an instance of an object
personA.name = tempNameandAddress[0];
personA.address = tempNameandAddress[1];
if ([self.container objectForKey:personA.name] == nil)
[self.container setObject:personA forKey:personA.name];
else
NSLog(#"%# already exists \n",personA.name);
}
This is simply an almost right common init.
self is set to the object returned by the superclass init.
Then they miss one proper step.
The next step should be if (self) { ...additional setup... }
Basically only creating ivars/properties if self as returned from super init is not nil.
If self is nil at that point you would normally just bypass additional code and go straight to return self. (Returning nil)
The next line is just creating the NSMutableArray ivar for the othercontainer property.
This is also not quite right.
In init, this is when you should use the synthesized ivar directly.
_othercontainer = [[NSMutableArray alloc] init];
Nothing special here.

NSdictionary to JSON data is being sorted

I have to send a data by post in JSON format. I have my nsdictionary with keys and values.
NSDictionary *params_country=[NSDictionary dictionaryWithObjectsAndKeys:
#"1111",#"#id",
nil];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"dummy3", #"#name",
#"dummy3#example.com", #"#mail",
#"password",#"#password", params_country,#"country",
nil];
When i am doing a log
DLog(#"params %#",[params description]);
I am getting the following
params {
"#mail" = "dummy3#example.com";
"#name" = dummy3;
"#password" = password;
}
The problem is that i have to sent the JSON in the order that i have listed in the above initialisation of my nsdictionary but the keys are being sorted somehow.
Any solution?
EDIT
Sorry i am sending a nsdictionary also in the params. If i remove the country then its fine.
Dictionaries are an unordered collection type. If you need to maintain a certain order, then you should use an ordered collection type like NSArray. But for this, your web service shouldn't care about the order, since it should be looking up the values by the keys provided.
As per some of the comments, this requirement does not match a valid JSON object as the official JSON Specification states:
An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).
Unfortunately we don't live in a perfect world with perfect web services and there are often certain things that are out of our control.
I wrote a subclass of NSMutableDictionary after reading up on the internet that will order the dictionary based on the order you call setValue:forKey:.
I put the class into a gist you can download from here: https://gist.github.com/liamnichols/7869468 or you can just copy it from below:
LNOrderedMutableDictionary.h
#interface LNOrderedMutableDictionary : NSMutableDictionary
///If `anObject` is nil, it will not be added to the dictionary.
- (void)setNothingIfNil:(id)anObject forKey:(id)aKey;
#end
LNOrderedMutableDictionary.m
#import "LNOrderedMutableDictionary.h"
#interface LNOrderedMutableDictionary ()
#property (nonatomic, strong) NSMutableDictionary *dictionary;
#property (nonatomic, strong) NSMutableOrderedSet *array;
#end
#implementation LNOrderedMutableDictionary
- (id)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
self.dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity];
self.array = [[NSMutableOrderedSet alloc] initWithCapacity:capacity];
}
return self;
}
- (id)init
{
self = [self initWithCapacity:0];
if (self)
{
}
return self;
}
- (void)setObject:(id)anObject forKey:(id)aKey
{
[self.array removeObject:aKey];
[self.array addObject:aKey];
[self.dictionary setObject:anObject forKey:aKey];
}
- (void)setNothingIfNil:(id)anObject forKey:(id)aKey
{
if (anObject != nil)
[self setObject:anObject forKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[self.dictionary removeObjectForKey:aKey];
[self.array removeObject:aKey];
}
- (NSUInteger)count
{
return [self.dictionary count];
}
- (id)objectForKey:(id)aKey
{
return [self.dictionary objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator
{
return [self.array objectEnumerator];
}
#end
If possible, your web service shouldn't have to rely on the JSON objects to be formatted in a specific order but if there is nothing you can do to change this then the above solution is what you are looking for.
Source: cocoawithlove

How to replace an object in an NSMutableArray at a given index with a new object

I have an NSMutableArray object (retained, synthesized as all) that is initiated just fine and I can easily add objects to it using the addObject: method. But if I want to replace an object at a certain index with a new one in that NSMutableArray, it doesn't work.
For example:
ClassA.h:
#interface ClassA : NSObject {
NSMutableArray *list;
}
#property (nonatomic, copy, readwrite) NSMutableArray *list;
#end
ClassA.m:
#import "ClassA.h"
#implementation ClassA
#synthesize list;
- (id)init
{
[super init];
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.list = localList;
[localList release];
//Add initial data
[list addObject:#"Hello "];
[list addObject:#"World"];
}
// Custom set accessor to ensure the new list is mutable
- (void)setList:(NSMutableArray *)newList
{
if (list != newList)
{
[list release];
list = [newList mutableCopy];
}
}
-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
int i = [theIndex intValue]-1;
[self.list replaceObjectAtIndex:i withObject:newTitle];
NSLog((NSString *)[self.list objectAtIndex:i]); // gives the correct output
}
However, the change remains true only inside the method. from any other method, the
NSLog((NSString *)[self.list objectAtIndex:i]);
gives the same old value.
How can I actually get the old object replaced with the new one at a specific index so that the change can be noticed from within any other method as well.
I even modified the method like this, but the result is the same:
-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
int i = [theIndex intValue]-1;
NSMutableArray *localList = [[NSMutableArray alloc] init];
localList = [localList mutableCopy];
for(int j = 0; j < [list count]; j++)
{
if(j == i)
{
[localList addObject:newTitle];
NSLog(#"j == 1");
NSLog([NSString stringWithFormat:#"%d", j]);
}
else
{
[localList addObject:(NSString *)[self.list objectAtIndex:j]];
}
}
[self.list release];
//self.list = [localList mutableCopy];
[self setList:localList];
[localList release];
}
Please help out guys :)
This does the trick:
[myMutableArray replaceObjectAtIndex:index withObject:newObject];
OK, there are a few bits of confusion here.
You don't need to take a mutableCopy of a newly created NSMutableArray to make it mutable. It's already mutable -- the clue is in the name. You only need to do that in the setter if you want the property to have copy semantics (which you've set, and may have good reason for, of course). But you certainly wouldn't need to do it as shown in your updated updateTitle code, and doing so leaks localList.
Also, you're mixing together property access via self.list and direct use of list in the same method. This is not invalid, but it's bad practice, because it means whatever other stuff the accessor methods do is being randomly bypassed. It's common for properties like this to do everything through self except in the accessors themselves, or in dealloc, and possibly in init (opinions seem to differ on this), where you would access the ivar directly.
Also, never call [self.list release] -- the property accessor doesn't give its caller ownership. Doing this will end in tears, mark my words.
None of this answers the real question, which is why is your change disappearing. The original updateTitle code does not explain this as far as I can see -- it should work. So I suspect that somewhere else you are calling self.list = theOriginalList and hence undoing your change.
Update:
Just for the sake of argument, I'm going to post what I think the code you posted is probably meant to look like. I've preserved your use of a string to pass the index to updateTitle, but I'd like to point out that doing it this way is wrong. It's a number, you should pass it as such. Even if the number comes from a text field or something, that's the caller's concern; the class interface should specify a number. Similarly the apparent change from 1-based to 0-based indexing. Please do not do this sort of thing implicitly, it is a recipe for weeping and gnashing of teeth.
ClassA.h:
#import <Cocoa/Cocoa.h>
#interface ClassA : NSObject
{
NSMutableArray* list;
}
- (void) setList:(NSMutableArray*)newList;
- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex;
#property (nonatomic, copy, readwrite) NSMutableArray* list;
#end
ClassA.m:
#import "ClassA.h"
#implementation ClassA
#synthesize list;
- (id) init
{
if ( self = [super init] )
{
list = [[NSMutableArray alloc] init];
[list addObject:#"Hello "];
[list addObject:#"World"];
}
return self;
}
- (void) setList:(NSMutableArray*) newList
{
if ( list != newList )
{
[list release];
list = [newList mutableCopy];
}
}
- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex
{
int i = [theIndex intValue] - 1;
[self.list replaceObjectAtIndex:i withObject:newTitle];
}
- (void) dealloc
{
[list release];
[super dealloc];
}
#end
This cleans up various issues, but note that updateTitle is mostly the same. If you drop all this in and the change still doesn't survive, you are definitely resetting list somewhere.
A more straight answer would be:
self.list[i] = newTitle;
This just works like
[self.list replaceObjectAtIndex:i withObject:newTitle];
Look at this line:
#property (nonatomic, copy, readwrite) NSMutableArray *list;
The copy means that whenever you access self.list, you don't get the "_list" instance variable of your object, but a copy of that list. If you write [self.list replaceObjectAtIndex... ] you replace an object in that copy of your list; the original _list is unchanged. Just use
#property (nonatomic, strong, readwrite) NSMutableArray *list;
And to avoid confusion, remove the "list" instance variable and the #synthesize statement, then use _list to access the instance variable.
For Swift you could try:
//if you have indexPath
self.readArray.removeAtIndex((indexPath?.row)!)
self.readArray.insert(tempDict, atIndex: (indexPath?.row)!)
//tempDict is NSDictionary object.
Finally Got Some Perfect Code,
let DuplicateArray: NSArray = array
let DuplicateMutableArray: NSMutableArray = []
DuplicateMutableArray.addObjectsFromArray(DuplicateArray as [AnyObject])
var dic = (DuplicateMutableArray[0] as! [NSObject : AnyObject])
dic["is_married"] = "false"
DuplicateMutableArray[self.SelectedIndexPath] = dic
array = []
array = (DuplicateMutableArray.copy() as? NSArray)!
//Output Will Be Like
array = [
{
"name": "Kavin",
"Age": 25,
"is_married": "false"
},
{
"name": "Kumar",
"Age": 25,
"is_married": "false"
}
]

Resources