Comparing two NSDictionaries and deleting if keys don't match - ios

In my app I'm using two NSMutableDictionaries. Lets call them basicDictionary and refreshDictionary.
In basicDictionary I have values, lets say
key:"1" value:"2000" photo:someUIImage //Lets say I want to track score of player
key:"2" value:"1500" photo:someUIImage2
key:"3" value:"1500" photo:someUIImage3
key:"4" value:"1500" photo:someUIImage4
key stands for player id and rest is pretty clear.
I do server api call every 20 sec to refresh score of players. Because I don't want to load photo as well im using refresh call to bring me only player id and his actual score.
So every 20 secs I get data that I save in refreshDictionary. There are only ids and current score of that id.
refreshDictionary example:
key:"1" value:"2500"
key:"2" value:"2800"
key:"3" value:"2700"
I update my tableView with new values. As you can see, I got data only for 3 players, because player 4 has deleted his profile. Now my question is, how do I update basicDictionary to remove player 4?
I know that I'm supposed tu use if (!([basicDictionary isEqualToDictionary:refreshDictionary))
however, it won't tell me at which key they aren't equal.
So what would you guys do? Should I iterate through both of them in nested loop? That seems to consume alot of time when dictionaries are bigger. Oh by the way, my dictionaries are always sorted same way (by players id)
My idea is, that I compare these two dictionaries in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (refresh)
{ //compare dictionaries and modify basicDictionary that is data source for my tableview by deleting where key doesn't match
}

this code will give you keys that changed:
NOTE: as the lookup of keys is basically jumping into a hashmap (an o1 op), you dont have to worry about the 2nd array walk you talk about
#import <Foundation/Foundation.h>
#interface NSDictionary (changedKeys)
- (NSArray*)changedKeysIn:(NSDictionary*)d;
#end
#implementation NSDictionary (changedKeys)
- (NSArray*)changedKeysIn:(NSDictionary*)d {
NSMutableArray *changedKs = [NSMutableArray array];
for(id k in self) {
if(![[self objectForKey:k] isEqual:[d objectForKey:k]])
[changedKs addObject:k];
}
return changedKs;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDictionary *d1 = #{#"1":#"value",#"2":#"value",#"3":#"value",#"4":#"value"};
NSDictionary *d2 = #{#"1":#"value",#"2":#"newvalue",#"3":#"value"};
NSArray *ks = [d1 changedKeysIn:d2];
NSLog(#"%#", ks);
}
return 0;
}
Edit: self.allKeys changed to just self -- a dictionary can be enumerated already

Related

containsObject is returning NO after the app is restarted

I have a UITabelView displaying a list of items(returned from the server), users will be able to select any item and add it to his own section.
Upon selecting an item, I am retrieving the selected item details and saving it to a pList file, the list will then be displaying a small icon informing the user that the item is added to his section. User will be able to remove it from his section by tapping the icon again.
To know if the item is already in his section, i am using
[self.myItemsArray containsObject:item]
Everything is working perfect if the user don't exit the application. The issue is occurring once the app is restarted. When I retrieve the tableview list from the database or from server, all the items will not be shown as on my list and [self.myItemsArray containsObject:item] will return NO for the previous added items.
I was searching for an alternative way, by creating an NSArray of my items id's and will then check if the new array will contains the item id to display the icon.
the new issue is that the id is returned as double from the server and stored as it is. The application is crashing when creating the array:
[self.myItemsArray valueForKey:#"Id"]
myItemsArray is an array of items(item is an NSDictionary containing all the details)
So i am desperate now,could anyone help me by resolving any of the above issues?
I prefer the first one, since containsObject will be more simple, but I don't mind to solve it with the second choice if the first will not work.
You'll need to make sure you have a good isEqual: method defined for your custom class, since that's what NSArray uses to determine containment: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/#//apple_ref/occ/instm/NSArray/containsObject:
Let's say this class has something like a 'key' or 'id' value that is unique for each instance of the class (which in your case is a double type). This will not always be the case, of course; it's often an aggregate of individual properties/ivars that constitutes a unique 'key', but for the purposes of this discussion let's say that such a field exists. Your code might look something like this (all in the .m):
static double const EPSILON = .000000001; // You'll need to be the judge of how "close" values can be to each other but still be distinct
#interface MyObjectClass: NSObject
#property (nonatomic, readonly) double uniqueKey;
#end
#implementation MyObjectClass
// ...
- (BOOL)isEqual:(id)object
{
if (self == object) { return YES; }
if (!object isKindOfClass:[MyObjectClass class]]) { return NO; }
MyObjectClass *myObject = (MyObjectClass *)object;
if (abs(self.uniqueKey - myObject.uniqueKey) < EPSILON) { return YES; }
return NO;
}
//...
#end
Note that you should NOT check for direct equality between two float or double values; see How dangerous is it to compare floating point values? for a good discussion about the pitfalls of working with floating point and double precision values.

Can we create an array dynamically in ios?

I have a user input in my app, according to which, the number of arrays that I need to create changes (user inputs the number of channels into which I need to split an incoming data stream, with one array for each channel). The number of channels can be any multiple of 4 up to 32. I want to know whether there is a way to automatically initialize arrays, perhaps using for loops like in this pseudocode:
for(initialization, numberOfChannels, increment) {
NSString (set array name to channel%dArray, i);
NSMutableArray *channeliArray = [[NSAMutablerray alloc] init];
}
Everywhere I've looked, I didn't find any way to set the name of the array dynamically and hence allow initializations like the one above. Even using a 2D array would have the same problem. Any help is appreciated. Thank you!
Just use a two dimensional array as per #Jonah's comments -
self.channels=[NSMutableArray new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels addObject:[NSMutableArray new]];
}
Then you can access a particular channel's array by index -
NSMutableArray *channel=[self.channels objectAtIndex:channelNumber];
Or, if you wanted to use channel names as per your pseudocode, use a dictionary of arrays
self.channels=[NSMutableDictionary new];
for (int i=0;i<numberOfChannels;i++) {
[self.channels setObject:[NSMutableArray new] forKey:[NSString stringWithFormat:#"channel%dArray",i]];
}
Then you can access a particular channel's array by name -
NSMutableArray *channelArray=[self.channels objectForKey:#"channel3Array"];

I need to build a HashMap like, Map<Set, Set>. Is it possible?

My usecase here is, I need to search for a string within a set of strings(say synonyms). If I find the relevant search-word within the set of strings, then I will display the set of values associated with that set of strings. I am currently thinking of implementing it as a Map, but not sure, if that's even possible.
I am confused about the best data-structure that can be used in this scenario?
Thanks for the help.
EDIT
Replaced Array with Set as it made more sense
Scenario:
Set-1 = {jug,jar,bottle,cup}
Set-2 = {"Store water", "Store Juice", "Store Coffee"}
For any input I receive from either jug,jar,bottle,cup, I should return the properties associated with it from Set-2. In simple words, I need to return whole of Set-2 when any word from Set-1 matches. Hopefully, this will make my question clear.
you can subclass NSObject for that kind of behaviour.
#interface Synonyms : NSObject
#property NSArray *strings;
#property NSArray *values;
-(NSArray *)searchForWord:(NSString *)searchWord;
#end
#implementation Synonyms
-(NSArray *)searchForWord:(NSString *)searchWord{
BOOL found=NO;
//add code to search for that search term in strings array
if(found)
return self.values;
else
return nil;
}
#end
so, first create all these synonyms instances and store them in NSArray.
Assign strings and values array to each instance.
When user enters search-word, call searchForWord function for each element of array.
If found ,it returns not nil object.else no synonym.
PART 2:
If you are looking for below kind of behaviour, example below
Dictionary myDic;
myDic.Add("car", "automobile");
myDic.Add("car", "autovehicle");
myDic.Add("car", "vehicle");
myDic.Add("bike", "vehicle");
myDic.ListOSyns("car") // should return {"automobile","autovehicle","vehicle" ± "car"}
// but "bike" should NOT be among the words returned
Then you can use NSDictionary,
NSDictionary *dictionary =#{#"car":#[#"automobile",#"autovehicle",#"vehicle"],#"bike":#[#"vehicle"]};
NSArray *values =[dictionary objectForKey:#"search-term"];
if(values==nil)
//no result
else
return values;

Objective-C how to pull several random items out of NSArray without getting duplicates? [duplicate]

This question already has answers here:
Getting a random object from NSArray without duplication
(3 answers)
Closed 7 years ago.
I have an array of random properties I would like to assign to equipment within the game I'm developing.
The code that I use below is returning an NSArray. I'm interested if there's way to get item indices from that array without getting duplicate values. The obvious solution is to create a mutable array with the returned array, do random, remove item that was returned and loop until the number of items is received.
But is there a different way of getting X random items from NSArray without getting duplicates?
//get possible enchantments
NSPredicate *p = [NSPredicate predicateWithFormat:#"type = %i AND grade >= %i", kEnchantmentArmor,armor.grade];
NSArray* possibleEnchantments = [[EquipmentGenerator allEnchantmentDictionary] objectForKey:#"enchantments"];
//get only applicable enchantments
NSArray *validEnchantments = [possibleEnchantments filteredArrayUsingPredicate:p];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:validEnchantments];
NSDictionary* enchantment = nil;
if(mutableArray.count>0)
{
//got enchantments, assign number and intensity based on grade
for (int i = 0; i<3;i++)
{
enchantment = mutableArray[arc4random()%mutableArray.count];
[mutableArray removeObject:enchantment];
//create enchantment from dictionary and assign to item.
}
}
You can shuffle the array using one of the following techniques:
What's the Best Way to Shuffle an NSMutableArray?
Non repeating random numbers
Then, take the first X elements from the array.
Many years ago, I was working on card game and I realized that shuffling the deck was an inefficient way to get random cards. What I would do in your shoes is pick a random element, and then replace it with the element at the end of the array, like so:
#interface NSMutableArray (pickAndShrink)
- (id) pullElementFromIndex:(int) index // pass in your random value here
{
id pickedItem = [self elementAtIndex:index];
[self replaceObjectAtIndex:index withObject:[self lastObject]];
[self removeLastObject];
return pickedItem;
}
#end
The array will shrink by one every time you pull an element this way.
You could use a random number generator to pick a starting index, and then pick the subsequent indices based on some kind of math function. You would still need to loop depending on how many properties you want.
Eg:
-(NSMutableArray*)getRandomPropertiesFromArray:(NSArray*)myArray
{
int lengthOfMyArray = myArray.count;
int startingIndex = arc4random()%lengthOfMyArray;
NSMutableArray *finalArray = [[NSMutableArray alloc]init]autorelease];
for(int i=0; i<numberOfPropertiesRequired; i++)
{
int index = [self computeIndex:i usingStartingIndex:startingIndex origninalArray:myArray];
[finalArray addObject:[myArray objectAtIndex:index]];
}
return finalArray;
}
-(int)computeIndex:(int)index usingStartingIndex:(int)startingIndex
{
//You write your custom function here. This is just an example.
//You will have to write some code to make use you don't pick an Index greater than the length of your array.
int computedIndex = startingIndex + index*2;
return startingIndex;
}
EDIT: Even your computeIndex function could use randomness in picking the subsequent indices. Since you have a startingIndex, and another index, you could use that to offset your function so that you never pick a duplicate.
EDIT: If your array is very large, and the subset you need to pick is small, then rather than shuffle the entire array (maybe more expensive), you could use this method to pick the number of items you need. But if your array is small, or if the number of items you need to pick are almost the size of the array, then the godel9's solution is better.
You can use a mutable array and then remove them as the are selected, use something like random()%array.count to get a random index. If you don't want to modify the array then copy it with [array mutableCopy].

Find the average of an NSMutableArray with valueForKeyPath

Currently I'm trying to find a compact way to average a matrix. The obvious solution is to sum the matrix, then divide by the number of elements. I have, however, come across a method on the apple developer website that claims this can be done in a simpler way using valueForKeyPath. This is linked here:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html
Here is the example I'm currently working on to try and get it to work:
-(void)arrayAverager
{
NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:25];
[myArray addObject:[NSNumber numberWithInteger:myValue]];
NSNumber *averageValue = [myArray valueForKeyPath:#"#avg.self"];
NSLog(#"avg = %#", averageValue);
}
The problem is: instead of averaging the array it merely prints out the elements in the array 1 by 1.
UPDATE
-(void) pixelAverager
{
//Put x-coordinate value of all wanted pixels into an array
NSMutableArray *xCoordinateArray = [NSMutableArray arrayWithCapacity:25];
[xCoordinateArray addObject:[NSNumber numberWithInteger:xCoordinate]];
NSLog(#"avg = %#", [xCoordinateArray valueForKeyPath:#"#avg.intValue"]);
}
You need to use #avg.floatValue (or #avg.doubleValue, or what have you). The #avg operator will average the property of the objects in the array specified by the name after the dot. The documentation is confusing on this point, but that is what:
to get the values specified by the property specified by the key path
to the right of the operator
Is saying. Since you have a collection of NSNumber objects, you use one of the *value accessors, e.g. floatValue to get the value of each object. As an example:
#include <Foundation/Foundation.h>
int main(void) {
NSMutableArray *ma = [NSMutableArray array];
[ma addObject:[NSNumber numberWithFloat:1.0]];
[ma addObject:[NSNumber numberWithFloat:2.0]];
[ma addObject:[NSNumber numberWithFloat:3.0]];
NSLog(#"avg = %#", [ma valueForKeyPath:#"#avg.floatValue"]);
return 0;
}
Compiling and running this code returns:
$ clang avg.m -framework Foundation -o avg
steve:~/code/tmp
$ ./avg
2013-01-18 12:33:15.500 avg[32190:707] avg = 2
steve:~/code/tmp
The nice thing about this approach is that this work for any collection, homogenous or otherwise, as long as all objects respond to the specified method, #avg will work.
EDIT
As pointed in the comments, the OP's problem is that he is averaging a collection with one element, and thus it appears to simply print the contents of the collection. For a collection of NSNumber objects, #avg.self works just fine.
No, you can't do like this. The object Transaction is a Modal Class. This class is having three properties, namely
payee
amount
date
Each row in this image represents one Transaction modal object.
transactions is an array which is holding all these rows (Transaction Modal Objects).
In these transactions array, they are trying to calculate the Transaction Modal amount field average using the operator #avg. So, its like
NSNumber *transactionAverage = [transactions valueForKeyPath:#"#avg.amount"];
your array doesn't have the key self. So that's the problem

Resources