Finding the minimum value using KVC across n-keys - objective-c

Stuck on KVCs in Obj-C again.
I am wanting to use KVC to find the minimum value across multiple keys.
Consider the following array:
NSArray *data = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:2.0], #"a", [NSNumber numberWithFloat:5.0], #"b", [NSNumber numberWithFloat:4.0], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:3.0], #"a", [NSNumber numberWithFloat:1.0], #"b", [NSNumber numberWithFloat:1.5], #"c", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:9.0], #"a", [NSNumber numberWithFloat:7.0], #"b", [NSNumber numberWithFloat:6.0], #"c", nil],
nil];
I can find the minimum value for 'a', 'b', or 'c' easily with:
float minKeyA = [[data valueForKeyPath:#"#min.a"] floatValue]; // value: 2.0
float minKeyB = [[data valueForKeyPath:#"#min.b"] floatValue]; // value: 1.0
float minKeyC = [[data valueForKeyPath:#"#min.c"] floatValue]; // value: 1.5
What I am wanting to achieve is to take a NSSet or NSArray of keys and find the minimum value across the pool of those keys.
NSSet *keySet1 = [NSSet setWithObjects:#"a", #"b", nil];
// use to find absolute minimum value across keys 'a' and 'b' --> desired value 1.0
NSSet *keySet2 = [NSSet setWithObjects:#"a", #"c", nil];
// use to find absolute minimum value across keys 'a' and 'c' --> desired value 1.5
NSSet *keySet3 = [NSSet setWithObjects:#"a", #"b", #"c", nil];
// use to find absolute minimum value across keys 'a', 'b', and 'c' --> desired value 1.0
Appreciate any pointers :)

A naive solution would be to first find the minimum value for each key and then find the minimum among those minimum values in a second step.
NSMutableSet *localMinima = [NSMutableSet setWithCapacity:[keySet1 count]];
for (NSString *key in keySet1) {
NSString *keyPath = [NSString stringWithFormat:#"#min.%#", key];
NSNumber *localMin = [data valueForKeyPath:keyPath];
[localMinima addObject:localMin];
}
NSNumber *globalMin = [localMinima valueForKeyPath:#"#min.self"];

Related

Convert NSarray to twodimensional array

I have a an one dimensional array which contains a vary numbers of object (depending on the userinput)
The NSArray is called homePlayersArray. This could example contain 2, 3, 5, 6, 4
The thing is i want to convert this to a two dimensional array where example.
{2,0}, {3,}, {5,0}, {6,0},{4,0}
the first value in the object will me by NSarray (called homepPlayersArray) and the second value will be 0.
What is the best way to obtain this?
//Your original array
NSArray *homePlayersArray = [[NSArray alloc] initWithObjects:
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:5],
[NSNumber numberWithInt:6],
[NSNumber numberWithInt:4],nil];
//For your 2D array
NSMutableArray *secondArray = [[NSMutableArray alloc] initWithCapacity:[homePlayersArray count]];
//populate as required
for(int i=0;i<[homePlayersArray count];i++){
NSArray *tempArray = [[NSArray alloc] initWithObjects:[homePlayersArray objectAtIndex:i],[NSNumber numberWithInt:0], nil];
[secondArray addObject:tempArray];
}
//print out some results to show it worked
NSLog(#"%#%#",#"secondArray first object value 0: ",[[secondArray objectAtIndex:0] objectAtIndex:0] );
NSLog(#"%#%#",#"secondArray first object value 1: ",[[secondArray objectAtIndex:0] objectAtIndex:1] );
NSLog(#"%#%#",#"secondArray second object value 0: ",[[secondArray objectAtIndex:1] objectAtIndex:0] );
NSLog(#"%#%#",#"secondArray second object value 1: ",[[secondArray objectAtIndex:1] objectAtIndex:1] );

NSPredicate 'OR' filtering based on an NSArray of keys

Consider the following NSArray:
NSArray *dataSet = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"abc", #"key1", #"def", #"key2", #"hij", #"key3", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"klm", #"key1", #"nop", #"key2", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"qrs", #"key2", #"tuv", #"key4", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"wxy", #"key3", nil],
nil];
I am able to filter this array to find dictionary objects that contain the key key1
// Filter our dataSet to only contain dictionary objects with a key of 'key1'
NSString *key = #"key1";
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"%# IN self.#allKeys", key];
NSArray *filteretSet1 = [dataSet filteredArrayUsingPredicate:key1Predicate];
NSLog(#"filteretSet1: %#",filteretSet1);
Which appropriately returns:
filteretSet1: (
{
key1 = abc;
key2 = def;
key3 = hij;
},
{
key1 = klm;
key2 = nop;
}
)
Now, I am wanting to filter the dataSet for dictionary objects containing ANY of the keys in an NSArray.
For example, using the array: NSArray *keySet = [NSArray arrayWithObjects:#"key1", #"key3", nil]; I want to create a predicate that returns and array of any dictionary objects that contain either 'key1' or 'key3' (ie. in this example all dictionary objects would be returned except for the third object - as it does not contain either 'key1' or 'key3').
Any ideas on how I would achieve this? Would I have to use a compound predicate?
The ANY operator of NSPredicate covers this:
NSSet *keys = [NSSet setWithObjects:#"key1", #"key3", nil];
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"any self.#allKeys in %#", keys];
Do this:
NSString *key = #"key1";
NSString *key1 = #"key3";
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"%# IN self.#allKeys OR %# IN self.#allKeys",key,key1];
NSArray *filteretSet1 = [dataSet filteredArrayUsingPredicate:key1Predicate];
NSLog(#"filteretSet1: %#",filteretSet1);
Works perfectly for me. Hope Helpful
Altough the question has been answered, you could also use block for more granularity:
NSArray *filter = [NSArray arrayWithObjects:#"key1", #"key3",nil];
NSPredicate *filterBlock = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSDictionary *data = (NSDictionary*)obj;
// use 'filter' and implement your logic and return YES or NO
}];
[dataSet filteredArrayUsingPredicate:filterBlock];
That could be rearranged as you want, maybe within its own method.

Using NSPredicate to filter based on multiple keys (NOT values for key)

I have the following NSArray containing NSDictionary(s):
NSArray *data = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], #"bill", [NSNumber numberWithInt:2], #"joe", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:3], #"bill", [NSNumber numberWithInt:4], #"joe", [NSNumber numberWithInt:5], #"jenny", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:6], #"joe", [NSNumber numberWithInt:1], #"jenny", nil],
nil];
I am wanting to create a filtered NSArray that only contains objects where the NSDictionary matches multiple 'keys' using NSPredicate.
For example:
filter the array to only contain the NSDictionary objects that have keys "bill" and "joe" [desired result: new NSArray would contain the first two NSDictionary objects]
filter the array to only contain the NSDictionary objects that have keys "joe" and "jenny" [desired result: new NSArray would contain the last two NSDictionary objects]
Can anyone please explain the format of the NSPredicate to achieve this?
Edit:
I can achieve a similar outcome to desired NSPredicate using:
NSMutableArray *filteredSet = [[NSMutableArray alloc] initWithCapacity:[data count]];
NSString *keySearch1 = [NSString stringWithString:#"bill"];
NSString *keySearch2 = [NSString stringWithString:#"joe"];
for (NSDictionary *currentDict in data){
// objectForKey will return nil if a key doesn't exists.
if ([currentDict objectForKey:keySearch1] && [currentDict objectForKey:keySearch2]){
[filteredSet addObject:currentDict];
}
}
NSLog(#"filteredSet: %#", filteredSet);
I'm imagining NSPredicate would be more elegant if one exists?
they only way I know is to combine two conditions like "'value1' IN list AND 'value2' IN list"
self.#allKeys should return all the keys of the dictionary (self is each dictionary in your array). If you don't write it with the prefix # then the dictionary will just look for a key that is "allKeys" instead of the method "- (NSArray*) allKeys"
The code:
NSArray* billAndJoe = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%# IN self.#allKeys AND %# IN self.#allKeys" , #"bill",#"joe" ]];
NSArray* joeAndJenny = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%# IN self.#allKeys AND %# IN self.#allKeys" , #"joe",#"jenny" ]]
Since a dictionary just returns nil if you ask for a value of a non-existing key, it is enough to specify that the value should be non-nil. A format like the following should cover your first case:
[NSPredicate predicateWithFormat: #"%K != nil AND %K != nil", #"bill", #"joe"]
The second case, with "joe" and "jenny" follows a similar pattern, of course.

Which type I should use to save many objects with same key? (iOS)

Which type I should use to save many objects with same key?
I should post data to server where one of parameter is suggestedTo and it contains userId.
This parameters should be more then one. So I'm confused which data type I should use to save them.
For example array or dictionary should looks like
{
#"suggestedTo" = 111,
#"suggestedTo" = 222,
#"suggestedTo" = 333,
etc.
}
This is typically handled with a dictionary of sets (or arrays if the data is ordered). So in this case, you'd have something like:
NSSet *suggestedTo = [NSSet setWithObjects:[NSNumber numberWithInt:111],
[NSNumber numberWithInt:222],
[NSNumber numberWithInt:333], nil];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:suggestedTo,
#"suggestedTo", nil];
You could use a dictionary of arrays
NSArray *suggestedTos = [[NSArray alloc] initWithObjects:
[NSNumber numberWithInt:111],
[NSNumber numberWithInt:222],
[NSNumber numberWithInt:333], nil];
NSDictionary *myDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
suggestedTos, #"suggestedTo", nil];

Whats the best way to convert an NSString to an NSInteger based on an array of values?

I want to convert characters into integers based on predetermined values, for example:
a = 0
b = 1
c = 2
d = 3
etc...
Right now I'm doing it with an If/Else If, I just want to know if there is a faster/better way I should be doing it because the list of conversions may get quite long.
Here's what I'm using now:
-(NSInteger)ConvertToInt:(NSString *)thestring {
NSInteger theint;
if([thestring isEqualToString:#"a"] == YES){
theint = 0;
} else if ([thestring isEqualToString:#"b"] == YES){
theint = 1;
} //etc...
return theint;
}
This works fine, but as I said, if it makes more sense can I create an array with all the key/values then just run through that to return the integers?
Please provide examples as I'm a beginner with Objective C/iOS. I come from Web languages.
Thanks!
EDIT: Thanks for the help everyone. I used taskinoors answer but I replaced the NSDictionary which was giving error messages with this:
NSDictionary *dict;
dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], #"a",
[NSNumber numberWithInt:1], #"b",
[NSNumber numberWithInt:2], #"c", nil];
unichar ch = [thestring characterAtIndex:0];
theint = ch - 'a';
Note that, 'a' with a single quote is character a, not string "a".
If the values are not regular like your example then you can store all predefined values into a dictionary. For example:
"a" = 5;
"b" = 1;
"c" = 102;
NSArray *values = [NSArray arrayWithObjects:[NSNumber numberWithInt:5],
[NSNumber numberWithInt:1], [NSNumber numberWithInt:102], nil];
NSArray *keys = [NSArray arrayWithObjects:#"a", #"b", #"c", nil];
NSDictionary *dic = [NSDictionary dictionaryWithObjects:values forKeys:keys];
theint = [[dic valueForKey:thestring] intValue];
If you wanted to keep some flexibility in what strings map to what integers, and your integers run from 0 to n-1 where you have n unique items in the array, you could do something like this:
-(NSInteger)ConvertToInt:(NSString *)thestring {
NSArray *arr = [NSArray arrayWithObjects:#"a", #"b", #"c", #"d", nil];
NSInteger theint = [arr indexOfObject:thestring];
return theint;
}
Now this will build the array each time, which would be very inefficient, the optimal way would be to build the array once in your class, and then just use a reference to that array with the indexOfObject method call.