NSMutableArray adds same NSDictionary multiple times - ios

I am looping through an array, getting two items and putting them in a dictionary, and then add them to an array, but it keeps adding the last item of the array for the number objects in the array, even though when I log the dictionary inside the loop it has the correct value. Here is my code
for (TFHppleElement *element in nodesArray) {
[nameAndHrefDict setObject:[element objectForKey:#"href"] forKey:#"href"];
[nameAndHrefDict setObject:[element text] forKey:#"name"];
NSLog(#"PRE: %#", nameAndHrefDict);
[arrayOfDicts addObject:nameAndHrefDict];
NSLog(#"IN: %#", arrayOfDicts);
}
In the log I see this
PRE: {
href = "/dining-choices/res/index.html";
name = "Commons Dining Hall";
}
IN: (
{
href = "/dining-choices/res/index.html";
name = "Commons Dining Hall";
}
PRE: {
href = "/dining-choices/res/sage.html";
name = "Russell Sage Dining Hall";
}
IN: (
{
href = "/dining-choices/res/sage.html";
name = "Russell Sage Dining Hall";
},
{
href = "/dining-choices/res/sage.html";
name = "Russell Sage Dining Hall";
}
)
What is happening?
But it add the last value of nodesArray 8 times, instead of each value, why?
Thanks for the help in advance.

Please try:
for (TFHppleElement *element in nodesArray) {
[arrayOfDicts addObject:[NSDictionary dictionaryWithObjectsAndKeys:[element objectForKey:#"href"], #"href", [element text], #"name", nil]];
}

Objects are added to arrays by reference. The array doesn't create a copy of the dictionary, it just keeps a record of which dictionary has been added. You're adding the same dictionary, nameAndHrefDict, to the array every time. So the array considers itself to hold the same object multiple times.
At each iteration you are changing the value of that object but it is still the same object.
So the underlying issue is identify versus value. An array is populated by identity. You're expecting it to populate by value.
To fix: copy the dictionary when you add it to the array, or just create a brand new dictionary from scratch each time.

You need to alloc and init the array each time, so that there is new memory for it and then it wont add the same array 8 times

You have to instantiate the dictionary in the loop : see the code
NSMutableArray *saveData = [[NSMutableArray alloc]init];
for (int i=0; i<records.count; i++) {
RecordTable *record = [records objectAtIndex:i];
NSString *addID = record.addr_id;
NSMutableDictionary *saveToDic = [[NSMutableDictionary alloc]init];
[saveToDic setValue:addID forKey:#"addr_id"];
[saveData addObject:saveToDic];
}

Related

Strange dictionary sort ios objective c

I want to do kind of a weird dictionary sort. I have non-unique values and keys and get something like this
NSArray *counts = [#"1",#"2",#"2",#"3",#"6",#"10"];
NSArray *names =[#"Jerry",#"Marge",#"Jerry",#"Marge",#"Jen",#"Mark"];
The output that I want is an descending ordered list by counts with unique names. I don't want lower values of the same person in my outputted arrays. The output should be.
sortedNames=[#"Mark",#"Jen",#"Marge",#"Jerry"]
sortedCounts=[#"10",#"6",#"3",#"2"];
I would really appreciate some help on this.
NSMutableArray *userNameArray = [[NSMutableArray alloc] init];
NSMutableArray *countArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in bigDick) {
NSString *nameString =[dict objectForKey:#"Name"];
NSString *countString =[dict objectForKey:#"Count"];
NSInteger countInt = [countString integerValue];
NSNumber *countNumber =[NSNumber numberWithInt:countInt];
[userNameArray addObject:nameString];
[countArray addObject:countNumber];
}
NSArray *namesAscending =[[userNameArray reverseObjectEnumerator]allObjects];
NSArray *countsAscending=[[countArray reverseObjectEnumerator]allObjects];
// Put the two arrays into a dictionary as keys and values
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:countsAscending forKeys:namesAscending];
// Sort the first array
NSArray *sortedCountArray = [[dictionary allValues] sortedArrayUsingSelector:#selector(compare:)];
// Sort the second array based on the sorted first array
// NSArray *sortedNameArray= [dictionary objectsForKeys:sortedCountArray notFoundMarker:[NSNull null]];
NSMutableArray *nameArray =[[NSMutableArray alloc] init];
for (int i=1; i<sortedCountArray.count; i++) {
NSString *name = [dictionary allKeysForObject:sortedCountArray[i]];
if (sortedCountArray[i]!=sortedCountArray[i-1]) {
[nameArray addObject:name];
}
}
an old method is to manual sort the array with numbers, by searching on every iteraton for the biggest value, and when you find the max value take the name from the other vector at index of the max number and move it in new vector...
max = counts[0];
counter = 0;
for (int i=0;i<counts.count;i++)
{
temp = counts[i];
if (max<temp)
max = temp;
counter = i;
}
[new_names addObject: [names objectAtIndex:counter]];
[new_numbers addObject: max];
[numbers removeObjectAtIndex: counter];
[names removeObjectAtIndex:counter];
Try something like this. It should work if you do it this way.
Important! do not remove elements in for from array that you count for the for length.
Your problem is in your algorithm design, if you step through it a line at a time in the debugger you should see what it does and where it goes wrong.
We're not here to write you code, but let's see if we can go through one step of an algorithm to help you one your way:
Useful fact: If you lookup a key in a dictionary and that key does not exist the return value will be nil.
From this: you can use a dictionary to keep track of the names you have seen paired with the highest score so far. You obtain a name,score pair, lookup the name in the dictionary - if you get nil its a new name with a new high score. If it's not nil its the currently known high score, so you can compare and update.
That's a rough algorithm, let's try it. Before we start rather than using literal strings for keys everywhere let's define some constants. This has the advantage that we won't mistype the strings, the compiler will spot if we mistype the constant names. These can be defined at the file level or within a method:
const NSString *kName = #"Name";
const NSString *kCount = #"Count";
Now to the code, in a method somewhere, we'll need a dictionary:
NSMutableDictionary *highScores = [NSMutableDictionary new]; // a single dictionary rather than your two arrays
Now start your loop as before:
for (NSDictionary *dict in bigDict) // same loop as your code
{
and extract the two values as before:
NSString *nameString = dict[kName]; // same as your code, but using modern syntax
NSInteger countInt = [dict[kCount] integerValue]; // condense two lines of your code into one
Now we can lookup the name in our dictionary:
NSNumber *currentScore = highScores[nameString]; // get current high score for user, if any
If the name exists as a key this will return the current associated value - the score in this case, if there is no matching key this will return nil. We can test for this in a single if:
if (currentScore == nil // not seen user before, no high score
|| currentScore.integerValue < countInt) // seen user, countInt is greater
{
The above condition will evaluate to true if we either need to add the name or update its score. Adding & updating a key/value pair is the same operation, so we just need the line:
highScores[nameString] = #(countInt); // add or update score for user
and a couple of braces to terminate the if and for:
}
}
Let's see what we have:
NSLog(#"Output: %#", highScores);
This outputs:
Output: {
Jen = 6;
Jerry = 2;
Marge = 3;
Mark = 10;
}
Which is a step in the right direction. (Note: the dictionary is not sorted, NSLog just displays the keys in sorted order.)
Make sure you understand why that works, copy the code and test it. Then try to design the next phase of the algorithm.
If you get stuck you can ask a new question showing the algorithm and code you've developed and someone will probably help. If you do this you should include a link to this question so people can see the history (and know you're not trying to get an app written for you through multiple questions!)
HTH
Try this.
sortedArray = [yourArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
After sort your array then remove duplicates using following.
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray: sortedArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];

How do i get unique contents from my NSMutableArray?

I have a UITableView and am displaying contents from my NSMutableArray. Following is array format
(
{
Name = "ANS";
VersionNo = 6;
},
{
Name = "O-Hydro";
Version = 6;
},
{
Name = "ANS";
Version = 6;
},
{
Name = "ANTIChorosAnticholinergic";
Version = 6;
}
)
From this I need to display only unique "Name" (like in this I can see 2 "ANS" I need only one).
How can I do this in iOS?
I tried following but its not working
uniqueArray= [[NSMutableSet setWithArray: groupDetails] allObjects];
but in this way I can do only for NSArray not NSMutableArray.
Pls help me
You can use following line of code to convert your NSArray to NSMutableArray,
NSArray *uniqueArray= [[NSMutableSet setWithArray:groupDetails] allObjects];
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithArray:uniqueArray];
You could simply add mutableCopy.
But wait, before you do it. Arrays and sets have two differences:
Arrays can contain duplicates, sets cannot.
Arrays are ordered, sets are not.
So doing what you are doing, you lose the duplicates (intentionally), but the order, too (probably not intentionally).
I do not know, whether this is important for you, but for other readers it might be. So it is the better approach to do that with NSOrderedSet instead of NSSet:
NSOrderedSet *uniqueList = [NSOrderedSet orderedSetWithArray:array];
In many cases an ordered set is exactly what you want. (Probably it has been from the very beginning and the usage of NSArray was wrong. But sometimes you get an array.) If you really want an array at the end of the day, you can reconvert it:
array = [uniqueList.array mutableCopy];
If you just want an array of unique name values, you can use #distinctUnionOfObjects with valueForKeyPath -
NSArray *uniqueArray=[groupDetails valueForKeyPath:#"#distinctUnionOfObjects.name"];
But if you want the array to contain the dictionaries that correspond to the unique names then you need to do a little more work -
NSMutableArray *uniqueArray=[NSMutableArray new];
NSMutableSet *nameSet=[NSMutableSet new];
for (NSDictionary *dict in groupDetails) {
NSString *name=dict[#"name"];
if (![nameSet containsObject:name]) {
[uniqueArray addObject:dict];
[nameSet addObject:name];
}
}

how add NSMutableDictionary object to other NSMutableDictionary at position last+1

I have a NSArray With NSMutableDictionary object inside this array like the following .What i want to get an object say at index:2 of this array and add that NSMutableDictionary object from the following array into other NSMutableDictionary already have say five elements at positions 6:
(
{
eventId = 2;
eventName = "Sweels ";
},
{
eventId = 1;
eventName = "Lakenge";
},
{
eventId = 23;
eventName = "Royta";
}
)
I am able to get the NSMutableDictionary object and and can extract the key and value and then can add but want to add whole object without extracting the key and values
{
eventId = 23;
eventName = "Royta";
}
You can initialize an NSMutableDictionary object with the keys and values contained in another given dictionary using this initializer:
- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary
Example:
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] initWithDictionary:extractedDict];
If You do not want to create a new NSMutableDictionary Object and just need to add Key/Values to an existing Object then Use this Method:
- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues
Example:
[newDict setValuesForKeysWithDictionary:extractedDict];
If you want all the values as single object than you must do this-
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObjectsFromArray:[paramsDictionary allValues]];
NSLog(#"%#",[paramsDictionary allValues]);

Multiple dictionaries within an array and Checking for duplicate keys - Objective C

I have an array which contains multiple Dictionaries each one with 3 keys (#"date", #"username", #"text").
What I want to check for, is whether the same user (#"username") exists in more than one dictionary in that Array. And, if she does, combine the text for those "duplicates" into one dictionary.
I have considered this answer to check for duplicates and this one
but I cannot figure out how to combine these two.
Jumping in here because although I think you should work on the code yourself first, I think Miro's answer is more complicated than the issue requires and though I like the idea of using predicates in Greg's answer, here's a 3rd solution that (1) wouldn't require you to change your data structure and (2) references the necessary loops...
The way I'd do it: Create an NSMutableArray then start adding the usernames in order. If the NSMutableArray already contains the username though, don't add another instance of the username, but instead merge the dictionary info.
ex.
// Note: I'm calling your array of user dictionaries userArray.
// Create a username array to store the usernames and check for duplicates
NSMutableArray *usernames = [[NSMutableArray alloc] init];
// Create a new userArray to store the updated dictionary info, merged
// entries et. al.
NSMutableArray *newUserArray = [[NSMutableArray alloc] init];
// Go through the array of user dictionaries
for (NSDictionary *userDict in userArray) {
// If the usernames array doesn't already contain the username,
// add it to both the usernames array and the newUserArray as is
if (![usernames containsObject:[userDict objectForKey:#"username"]]) {
[usernames addObject:[userDict objectForKey:#"username"]];
[newUserArray addObject:userDict];
}
// Otherwise, merge the userArray entries
else {
// Get a mutable copy of the dictionary entry at the first instance
// with this username
int indexOfFirstInstance = [usernames indexOfObject:[userDict objectForKey:#"username"]];
NSMutableDictionary *entry = [[newUserArray objectAtIndex:indexOfFirstInstance] mutableCopy];
// Then combine the "text" or whatever other values you wanted to combine
// by replacing the "text" value with the combined text.
// (I've done so with a comma, but you could also store the value in an array)
[entry setValue:[[entry objectForKey:#"text"] stringByAppendingString:[NSString stringWithFormat:#", %#", [userDict objectForKey:#"text"]]] forKey:#"text"];
// Then replace this newly merged dictionary with the one at the
// first instance
[newUserArray replaceObjectAtIndex:indexOfFirstInstance withObject:entry];
}
}
Maybe something like this [untested] example? Loop through, maintain a hash of existing items, and if a duplicate is found then combine with existing and remove.
NSMutableArray main; // this should exist, with content
NSMutableDictionary *hash = [[NSMutableDictionary alloc] init];
// loop through, backwards, as we're attempting to modify array in place (risky)
for(int i = [main count] - 1; i >= 0; i--){
// check for existing
if(hash[main[i][#"username"]] != nil){
int existingIdx = [hash[main[i][#"username"]] integerValue]; // get existing location
main[existingIdx][#"text"] = [main[existingIdx][#"text"] stringByAppendingString:main[i][#"text"]]; // "combine text" .. or however you'd like to
[main removeObjectAtIndex:i]; // remove duplicate
} else {
[hash setValue:[[NSNumber alloc] initWithInt:i] forKey:main[i][#"username"]]; // mark existance, with location
}
}
If you use NSMutableDictionary, NSMutableArray and NSMutableString you can do it with predicate like that:
NSMutableDictionary *d1 = [#{#"username": #"Greg", #"text" : [#"text 1" mutableCopy]} mutableCopy];
NSMutableDictionary *d2 = [#{#"username": #"Greg", #"text" : [#"text 2" mutableCopy]} mutableCopy];
NSMutableDictionary *d3 = [#{#"username": #"John", #"text" : [#"text 3" mutableCopy]} mutableCopy];
NSMutableArray *array = [#[d1, d2, d3] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username = %#", #"Greg"];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary * firstDict = filterArray[0];
for (NSDictionary *d in filterArray)
{
if (firstDict != d)
{
[firstDict[#"text"] appendString:d[#"text"]];
[array removeObject:d];
}
}

How to delete an entry from NSDictionary which is the element of NSDictionary?

I have a problem which I can't solve for a long time. I have a JSON response from the server which is parsed to NSDictionary lastMsgs as in the image below:
So for example 1323 it's a key and it associated with NSDictionary (which contains keys such as body, subject etc and values). So the problem I need in some way delete an entry which nested NSDictionary value has entry : type = 1. I don't know how to do this. I tried to do this:
NSMutableArray* _ModelVals = [[lastMsgs allValues] mutableCopy];
for (int i =0; i<[_ModelVals count]; i++) {
string_compare = [NSString stringWithFormat:#"%#" , [_ModelVals objectAtIndex:i]];
if ([string_compare rangeOfString:#"type = 1"].location != NSNotFound) {
[_ModelVals removeObjectAtIndex:i];
}
}
But it is work not correctly and delete not all entries which has type = 1. So the question - how can I implement this and delete entry in nested NSDictionary?
There is no value "type = 1" in the dictionary. That's just the log. You get the value of a key in a dictionary using [dict objectForKey:#"key"] or dict[#"key"].
Judging from your log, the type seems to be an NSNumber, not an NSString. Just get the int representation of it (assuming the type is an integer) and use a simple C int to int comparison.
And you can't filter an array like that. You will skip an entry. If you remove an entry, you have to decrease i by 1.
Or use this simpler solution:
NSSet *keys = [lastMsgs keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
return [obj[#"type"] intValue] == 1;
}];
NSMutableDictionary *dict = [lastMsgs mutableCopy];
[dict removeObjectsForKeys:[keys allObjects]];
This will first collect the keys of all objects (dictionaries) that have a type of 1 and then remove those from a mutable copy of the original dictionary.
You cannot add or remove objects from a collection while enumerating though it. I would create a another array that you can store references to the objects that you want to delete and remove them after you have looped though it.

Resources