Reusable method to check if all objects are unequal using valueForKey - objective-c

I have been working with this method for hours. It is supposed to simply check if the attribute of the cards passed in as otherCards isEqual to the same attribute of this (self) instance of class. But I am tearing my hair out - it is giving false results
(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSArray *values = [NSArray arrayWithArray:[otherCards valueForKey:key]];
int countUnequalMatches = 0;
for (NSNumber *setValue in values) {
if (![[self valueForKey:key] isEqual:setValue]) {
countUnequalMatches++;}
}
NSLog(#"countUnequalMatches %d values count: %d", countUnequalMatches, [values count]);
if (countUnequalMatches == [values count]) return YES ?: NO;
}
It is called like this:
if ([self otherCards:otherCards allHaveUnequalAttribute:CARD_SHAPE]) {
NSLog(#"All unequal");
} else {
NSLog(#"One or more card equal");
}

I found the bug myself
if (countUnequalMatches == [values count]) return YES ?: NO;
should be:
if (countUnequalMatches == [otherCards count]) return YES ?: NO;
as I used the Set as an way to count unique items.

This is the sort of thing that predicates are very well suited to handle.
-(BOOL)otherCards: (NSArray *)otherCards allHaveUnequalAttribute: (NSString *)key
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K == %#", key, [self valueForKey:key]];
NSArray *equalCards = [otherCards filteredArrayUsingPredicate:pred];
return equalCards.count == 0;
}

Related

NSPredicate - predicateWithFormat insecure

I have a predicate for query in core data base but i don't know what is the correct way to validate its params?
- (void) queryToDatabaseWithStoreId:(NSInteger) storeId {
[NSPredicate predicateWithFormat:#"store.storeId = %d", storeId];
}
My question is how can i validate storeId param or what i need to use for that vulnerability to dissapear?
And if i have a list:
- (void) queryToDataBaseWithListStore:(NSArray<Store *> *) storeList {
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [storeList valueForObject:#"storeId"]];
}
https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/ValidatingInput.html#//apple_ref/doc/uid/TP40007246-SW3
I need avoid that:
The following commonly-used functions and methods are subject to format-string attacks:
Standard C
printf and other functions listed on the printf(3) manual page
sscanf and other functions listed on the scanf(3) manual page
syslog and vsyslog
Carbon
AEBuildDesc and vAEBuildDesc
AEBuildParameters and vAEBuildParameters
AEBuildAppleEvent and vAEBuildAppleEvent
Core Foundation
CFStringCreateWithFormat
CFStringCreateWithFormatAndArguments
CFStringAppendFormat
CFStringAppendFormatAndArguments
Cocoa
stringWithFormat:, initWithFormat:, and other NSString methods that take formatted strings as arguments
appendFormat: in the NSMutableString class
alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat: in NSAlert
predicateWithFormat:, predicateWithFormat:arguments:, and predicateWithFormat:argumentArray: in NSPredicate
raise:format: and raise:format:arguments: in NSException
NSRunAlertPanel and other AppKit functions that create or return panels or sheets
What is the best way to avoid this attack?
I have programmed this class but i don't know if it is enough.
#implementation StringUtils
+ (BOOL) isEmpty:(id) text {
if ([text isKindOfClass:[NSNull class]]) {
return YES;
} else {
if (text) {
if ([text isKindOfClass:[NSString class]]) {
NSString *textStr = [NSString stringWithFormat:#"%#", text];
return [textStr isEqualToString:#""];
}
return YES;
} else {
return YES;
}
}
}
+ (NSString *) validateField:(id) text {
NSInteger numErrors = 0;
NSString *pattern = #"[^A-Za-z0-9-]+";
NSError *error = nil;
NSString *textValidated = #"";
if ([text isKindOfClass:[NSNumber class]]) {
textValidated = [text stringValue];
} else if ([text isKindOfClass:[NSString class]]) {
textValidated = text;
} else {
#try {
textValidated = [text stringValue];
} #catch (NSException *exception) {
numErrors=+1;
}
}
//Only numbers && chars && -
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
NSRange textRange = NSMakeRange(0, textValidated.length);
NSRange matchRange = [regex rangeOfFirstMatchInString:textValidated options:NSMatchingReportProgress range:textRange];
if (matchRange.location != NSNotFound) {
numErrors+=1;
}
//Not empty string
if ([StringUtils isEmpty:textValidated]) {
numErrors+=1;
}
if (numErrors == 0) {
return textValidated;
}
return #"";
}
+ (NSArray *) validateArrayFields:(NSArray *) list {
NSInteger *numErrors = 0;
for (id obj in list) {
if ([StringUtils isEmpty:[StringUtils validateField:obj]]) {
numErrors+=1;
}
}
if (numErrors == 0) {
return list;
}
return [[NSArray alloc] init];
}
#end
For use normal:
[NSPredicate predicateWithFormat:#"store.storeId = %#", [StringUtils validateField:storeId]];
For use with array:
[NSPredicate predicateWithFormat:#"store.storeId IN %#", [StringUtils validateArrayFields:storeId]];

How to use an NSOutlineview with Coredata, grouping entities by their attributes?

I'm fried. I've been stuck on this for two days, googling, and reading, googling and reading.
I have an entity called Session and with an attribute called sessionYear (NSNumber).
I'd like to create an NSOutlineView that will group them by year and then sort them by month, sessionMonth (NSString). Like this:
1984
October
November
December
1989
January
February
2002
March
July
October
I've found lots of information about grouping entities under other entities, and various methods of using array controllers and dictionaries. Unfortunately, much of what I found was outdated or not ostensibly applicable to my situation. I'm new to developing and coredata in general, and would appreciate any guidance on this.
Again, the years are attributes of the entities.
Eventually, I want to be able to click on these entities and populate a tableview. I'd be interested in doing this with bindings if possible, but I've been unable to find solid information. Any help, or links to resources are appreciated.
EDIT:
I've used the NSOutlineViewDelegate approach with the appending four methods. However, now it seems the sessionMonths appear in the view when the year is expanded and then immediately disappear. If another (different) year is expanded all sessionMonths appear where they should and again immediately disappear. I've come so far... for only a glimpse. Any suggestions about where to start?
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
if (item == nil) {
return [savedSessionsOutlineViewData count];
}
return [item count];
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
if (item == nil) {
item = savedSessionsOutlineViewData;
}
if ([item isKindOfClass:[NSMutableArray class]]) {
return [item objectAtIndex:index];
}
else if ([item isKindOfClass:[NSDictionary class]]) {
NSArray *keys = [item allKeys];
return [item objectForKey:[keys objectAtIndex:index]];
}
return nil;
}
- (id)outlineView:(NSOutlineView *)outline objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item {
// If the item is a "yearArray" holding sessions.
if ([item isKindOfClass:[NSMutableArray class]]) {
NSArray *keys = [savedSessionsOutlineViewData allKeysForObject:item];
return [keys objectAtIndex:0];
} else if ([item isKindOfClass:[Session class]]) {
//NSLog (#"Item returned isKindOfClass:Session");
return [item valueForKey:#"sessionMonth"];
}
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
if ([item isKindOfClass:[NSMutableArray class]] || [item isKindOfClass:[NSDictionary class]]) {
if ([item count] > 0) {
return YES;
}
}
return nil;
}
In the attributes inspector for the NSOutline view, set the highlight to source list.
The best approach may be to create separate arrays for the top level groups and for each set of children. So get a list of all the unique years you want to display. You could use the collection operator #distinctUnionOfObjects.sessionYear to get an array of NSNumbers from the existing array of Session(s).
Some sample code below - I have not run this but took some code from an existing app and renamed to fit your model. There may be some mistakes..
#implementation SessionOutlineViewController <NSOutlineViewDataSource> {
NSArray *sortedYearsArray;
NSMutableDictionary *childrenDictionary; // for each key (year) we put an array of sessions in this dictionary
}
#end
#implementation SessionOutlineViewController
- (void)initialise {
NSArray* sessions = [self getData:#"Session" sortKey:#"date" predicate:nil];
// Get an array of distinct years
NSArray *yearsArray = [sessions valueForKeyPath:#"#distinctUnionOfObjects.sessionYear"];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"description" ascending:YES];
NSArray * sortedYearsArray =[yearsArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
for (NSNumber year in sortedYearsArray) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sessionYear == %#", year];
NSArray * sessionForYear = [self getData:#"Session" sortKey:#"date" predicate:predicate];
[childrenDictionary setObject:sessionsForYear forKey:year];
}
}
- (NSArray*)getData:(NSString*)entityName sortKey:(NSString*)sortKey predicate:(NSPredicate*)predicate
{
NSFetchRequest *req = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:_managedObjectContext];
if (entity == nil) {
NSLog(#" error entity %# NOT FOUND!", entityName);
return nil;
}
[req setEntity:entity];
[req setIncludesPropertyValues:YES];
if (predicate != nil)
[req setPredicate:predicate];
if (sortKey != nil) {
NSSortDescriptor *indexSort = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:YES];
NSArray *sorters = [NSArray arrayWithObject:indexSort]; indexSort = nil;
[req setSortDescriptors:sorters];
}
NSError *error;
NSArray *result = [managedObjectContext executeFetchRequest:req error:&error];
if (result == nil)
return nil;
return result;
}
#end
#implementation SessionOutlineViewController (NSOutlineViewDataSource)
- (NSArray *)childrenForItem:(id)item {
NSArray *children;
if (item == nil) {
children = sortedYearsArray;
} else {
children = [childrenDictionary objectForKey:item];
}
return children;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
return [[self childrenForItem:item] objectAtIndex:index];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
if ([outlineView parentForItem:item] == nil) {
return YES;
} else {
return NO;
}
}
- (NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return [[self childrenForItem:item] count];
}
#end

How to return arrays object + count IOS

hi at all ,I've this code :
+(NSArray *)splatterUrls
{
NSString *jsonString = [ ApiMethod jsonOfStores];
NSDictionary *results =[jsonString objectFromJSONString];
NSArray *movieArray = [results objectForKey:#"Seasons"];
//int i=0;
// Search for year to match
for (NSDictionary *movie in movieArray)
{
NSNumber *idSplatterMovie = [movie objectForKey:#"Id"];
// NSLog(#" %#", idSplatterMovie );
NSArray *try = [movie objectForKey:#"Episodes"];
// NSLog(#"%#", try);
for (NSDictionary *op in try)
{
if([idSplatterMovie integerValue] == 46)
{
//i++;
NSArray *movieArrayString = [op objectForKey:#"Url"];
// NSLog(#" %#", movieArrayString);
return movieArrayString;
}
}
}
}
I want to return movieArrayString with all his objects and how many object contains in it. I think that I should use this method : + (id)arrayWithObjects:(const id *)objects count:(NSUInteger)count. It's possible? If yes, can you tell me how can use it?
Thank you so much!
by the way , i have to call splatterUrls method and implement in home.m that it is :
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *urlSplatter= [GetSplatterUrlsMovie splatterUrls];
NSLog(#" %#", urlSplatter);
}
Looks good as it is to me.
Do this to return your movies array, array will be equal to your movies array:
NSArray *array = [self splatterUrls];
Then to get the count/number of objects in your array do this, i is equal to the number of objects in the array:
int i = [array count];
What is the problem ??
You return a NSarray ... call the method count on your NSarray object!

ARC conversion issue with handleSearchForTerm delegate method

since converting to ARC (automatically) i have noticed with my uisearchviewcontroller delegate an issue somewhere (i think in the first if statement below). It was changed as a result but either way my app crashes when i try to perform a search:
The current code:
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (KABrand *currentBrand in [self brands])
{
if ([currentBrand.name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
if (![searchResults containsObject:currentBrand])
[[self searchResults] addObject:currentBrand];
}
}
}
The previous code:
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (KABrand *currentBrand in [self brands])
{
if ([currentBrand.name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
if (![searchResults containsObject:currentBrand])
[[self searchResults] addObject:currentBrand];
}
}
}
}
Thanks for your help in advance!
Thanks guys
Here is one potential problem: -rangeOfString:options: may return an NSNotFound. But you are checking if the range.location != NSNotFound.
If is if the return value of -rangeOfString:options: is NSNotFound, your if statement becomes NSNotFound.location != NSNotFound. I don't believe that's allowed.

Replace all NSNull objects in an NSDictionary

I'm curious, I currently have an NSDictionary where some values are set to an NSNull object thanks to the help of json-framework.
The aim is to strip all NSNull values and replace it with an empty string.
I'm sure someone has done this somewhere? No doubt it is probably a four liner and is simple, I am just far too burnt out to figure this out on my own.
I've made a few changes to Jacob's original answer to extend it to handle dictionaries and arrays stored within the original dictionary.
#import "NSDictionary+NullReplacement.h"
#import "NSArray+NullReplacement.h"
#implementation NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced setObject:blank forKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByReplacingNullsWithBlanks] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByReplacingNullsWithBlanks] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
#end
And there's also an array category of course:
#import "NSArray+NullReplacement.h"
#import "NSDictionary+NullReplacement.h"
#implementation NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for (int idx = 0; idx < [replaced count]; idx++) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced replaceObjectAtIndex:idx withObject:blank];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByReplacingNullsWithBlanks]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByReplacingNullsWithBlanks]];
}
return [replaced copy];
}
#end
With this, you can take any array or dictionary and recursively wipe out all the [NSNull null] instances.
P.S. For completion's sake, here are the header files:
#interface NSDictionary (NullReplacement)
- (NSDictionary *)dictionaryByReplacingNullsWithBlanks;
#end
And the array header:
#interface NSArray (NullReplacement)
- (NSArray *)arrayByReplacingNullsWithBlanks;
#end
Really simple:
#interface NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings;
#end
#implementation NSDictionary (JRAdditions)
- (NSDictionary *)dictionaryByReplacingNullsWithStrings {
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
#end
Usage:
NSDictionary *someDictThatHasNulls = ...;
NSDictionary *replacedDict = [someDictThatHasNulls dictionaryByReplacingNullsWithStrings];
Rolling through the dictionary hunting for NSNull is one way to tackle the problem, but I took a slightly lazier approach. Instead of nil you could assign an empty string, but the principle is the same.
CPJSONDictionary.h
#interface NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey;
#end
CPJSONDictionary.m
#implementation NSDictionary (CPJSONDictionary)
- (id)jsonObjectForKey:(id)aKey {
id object = [self objectForKey:aKey];
if ([object isKindOfClass:[NSNull class]]) {
object = nil;
}
return object;
}
#end
I have tested Stakenborg solution. It works well, but it has following problem. If some object is expected to be number, for instance, converting it to NSNull can be a source of error.
I have create a new method to directly remove the NSNull entries. This way you only have to check that correspondant key exists.
Add in NSDictionary+NullReplacement
- (NSDictionary *)dictionaryByRemovingNulls{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (NSString *key in self) {
id object = [self objectForKey:key];
if (object == nul) [replaced removeObjectForKey:key];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced setObject:[object dictionaryByRemovingNulls] forKey:key];
else if ([object isKindOfClass:[NSArray class]]) [replaced setObject:[object arrayByRemovingNulls] forKey:key];
}
return [NSDictionary dictionaryWithDictionary:[replaced copy]];
}
And in NSArray+NullReplacement
- (NSArray *)arrayByRemovingNulls {
NSMutableArray *replaced = [self mutableCopy];
const id nul = [NSNull null];
for (int idx = [replaced count]-1; idx >=0; idx--) {
id object = [replaced objectAtIndex:idx];
if (object == nul) [replaced removeObjectAtIndex:idx];
else if ([object isKindOfClass:[NSDictionary class]]) [replaced replaceObjectAtIndex:idx withObject:[object dictionaryByRemovingNulls]];
else if ([object isKindOfClass:[NSArray class]]) [replaced replaceObjectAtIndex:idx withObject:[object arrayByRemovingNulls]];
}
return [replaced copy];
}
another variation:
NSDictionary * NewDictionaryReplacingNSNullWithEmptyNSString(NSDictionary * dict) {
NSMutableDictionary * const m = [dict mutableCopy];
NSString * const empty = #"";
id const nul = [NSNull null];
NSArray * const keys = [m allKeys];
for (NSUInteger idx = 0, count = [keys count]; idx < count; ++idx) {
id const key = [keys objectAtIndex:idx];
id const obj = [m objectForKey:key];
if (nul == obj) {
[m setObject:empty forKey:key];
}
}
NSDictionary * result = [m copy];
[m release];
return result;
}
The result is the same as, and it appears pretty much identical to Jacob's, but the speed and memory requirements are one half to one third (ARC or MRC) in the tests I made. Of course, you could also use it as a category method as well.
Here is my solution:
+ (NSDictionary *)cleanNullInJsonDic:(NSDictionary *)dic
{
if (!dic || (id)dic == [NSNull null])
{
return dic;
}
NSMutableDictionary *mulDic = [[NSMutableDictionary alloc] init];
for (NSString *key in [dic allKeys])
{
NSObject *obj = dic[key];
if (!obj || obj == [NSNull null])
{
// [mulDic setObject:[#"" JSONValue] forKey:key];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
[mulDic setObject:[self cleanNullInJsonDic:(NSDictionary *)obj] forKey:key];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *array = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulDic setObject:array forKey:key];
}else
{
[mulDic setObject:obj forKey:key];
}
}
return mulDic;
}
+ (NSArray *)cleanNullInJsonArray:(NSArray *)array
{
if (!array || (id)array == [NSNull null])
{
return array;
}
NSMutableArray *mulArray = [[NSMutableArray alloc] init];
for (NSObject *obj in array)
{
if (!obj || obj == [NSNull null])
{
// [mulArray addObject:[#"" JSONValue]];
}else if ([obj isKindOfClass:[NSDictionary class]])
{
NSDictionary *dic = [self cleanNullInJsonDic:(NSDictionary *)obj];
[mulArray addObject:dic];
}else if ([obj isKindOfClass:[NSArray class]])
{
NSArray *a = [BasicObject cleanNullInJsonArray:(NSArray *)obj];
[mulArray addObject:a];
}else
{
[mulArray addObject:obj];
}
}
return mulArray;
}
-(NSDictionary*)stripNulls:(NSDictionary*)dict{
NSMutableDictionary *returnDict = [NSMutableDictionary new];
NSArray *allKeys = [dict allKeys];
NSArray *allValues = [dict allValues];
for (int i=0; i<[allValues count]; i++) {
if([allValues objectAtIndex:i] == (NSString*)[NSNull null]){
[returnDict setValue:#"" forKey:[allKeys objectAtIndex:i]];
}
else
[returnDict setValue:[allValues objectAtIndex:i] forKey:[allKeys objectAtIndex:i]];
}
return returnDict;
}
A category on nsnull that returns nil seems to also sense, at least to me. There are a few out there. One makes all calls return nil which seems to make sense. Sorry no link. I guess if you need to later use nspropertylistserialization the category might not work for you.