NSUSerDefaults with NSMutableArray sort by desc order - objective-c

I'm breaking my head on why descending order sort is not working with the following code. I wanted to limit by top 5 scores and other logic. The scores would look like this: 22/30, 12/18, 34/38, 23/32 etc. I added/removed SortDescriptor to sort by descending order and it seems to work for the first 3 items but then is not sorting properly. Can somebody help?
- (NSMutableArray*) method1:(NSString *) mode byScore: (NSString *) score
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[defaults objectForKey:mode]mutableCopy];
if (!array)
{
array = [[NSMutableArray alloc] init];
}
NSLog(#"The content of array is%#", array);
if ([array count] < 5)
{
if (![array containsObject:score])
{
[array addObject:score];
// Need to sort here. But not too sure this is the right place
NSLog(#"The content of the sorted array upto 5 is%#", array);
}
}
else
{
if (![array containsObject:score])
{
if ([array lastObject] < score)
{
[array addObject:score];
// Need to sort here before I remove the last object
[array removeLastObject];
NSLog(#"The content of the sorted array else is%#",array);
}
}
}
[defaults setObject:array forKey:mode];
[defaults synchronize];
// I want the array in NSUserDefaults to be sorted in desc order
// don't know what to return here ==> the array object or the defaults object cast to NSMutableArray?
}

Helper function
static NSComparisonResult CompareFloats( float a, float b )
{
if ( a < b ) { return NSOrderedAscending ; }
else if ( a > b ) { return NSOrderedDescending ; }
return NSOrderedSame ;
}
Category on NSString
#implementation NSString (Stuff)
-(float)floatValueForFraction
{
NSArray * components = [ self componentsSeparatedByString:#"/" ] ;
return [ components[0] floatValue ] / [ components[1] floatValue ] ;
}
#end
Your method:
- (void)addScore:(NSString*)score forMode:(NSString*)mode
{
NSUserDefaults * defaults = [ NSUserDefaults standardUserDefaults ] ;
NSArray * scores = [ defaults objectForKey:mode ] ;
scores = scores ? [ scores arrayByAddingObject:score ] : #[ score ] ;
scores = [ scores sortedArrayUsingComparator:^(NSString * a, NSString * b){
return CompareFloats( [ a floatValueForFraction ], [ b floatValueForFraction ] ) ;
}]
if ( scores.count > 5 ) { scores = [ scores subarrayWithRange:(NSRange){ .length = 5 } ] ; }
[ default setObject:scores forKey:mode ] ;
}
If you want the updated high scores after calling this method, just use [ [ NSUserDefaults standardUserDefaults ] objectForKey:<mode> ]. It's better to have your methods just do one thing.

One approach to sorting array:
First define a block getNumeratorAndDenominatorFromScoreString as follows:
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
Then use this block together with -[NSArray sortedArrayUsingComparator] to sort array:
NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
This will order array from least to greatest. To order from greatest to least, just replace
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
with
if (value1 > value2) {
res = NSOrderedAscending;
} else if (value1 < value2) {
res = NSOrderedDescending;
}
A readable structure for this method would be, in [mostly not] pseudocode
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = //as above
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
//as above
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}
If you need to screen out scores the strings for which are not equal but the evaluations of the fractions for which are equal (#"1/2" and #"5/10"), you'll need to be more clever.
Here is the full code sketched out above:
- (void)addScoreToHighscores:(NSString *)score withMethod:(NSString *)mode
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *currentHighscores = [defaults arrayForKey:mode];
if (!currentHighscores) currentHighscores = [NSArray array];
if (![currentHighscores containsObject:score]) {
currentHighscores = [currentHighscores arrayByAddingObject:score];
//sort currentHighscores: adapt the above code so that we have
BOOL (^getNumeratorAndDenominatorFromScoreString)(NSString *, NSInteger *, NSInteger *) = ^(NSString *scoreString, NSInteger *numeratorOut, NSInteger *denominatorOut) {
BOOL res = NO;
NSArray *components = [scoreString componentsSeparatedByString:#"/"];
if (components &&
[components count] == 2) {
res = YES;
if (numeratorOut) {
NSNumber *numeratorNumber = [components objectAtIndex:0];
*numeratorOut = [numeratorNumber integerValue];
}
if (denominatorOut) {
NSNumber *denominatorNumber = [components objectAtIndex:1];
*denominatorOut = [denominatorNumber integerValue];
}
}
return res;
};
NSArray *newHighscores = [currentHighscores sortedArrayUsingComparator:^(id obj1, id obj2) {
NSComparisonResult res = NSOrderedSame;
NSString *score1 = (NSString *)obj1;
NSString *score2 = (NSString *)obj2;
NSInteger numerator1, denominator1, numerator2, denominator2;
BOOL res1, res2;
res1 = getNumeratorAndDenominatorFromScoreString(score1, &numerator1, &denominator1);
res2 = getNumeratorAndDenominatorFromScoreString(score2, &numerator2, &denominator2);
if (res1
&& res2) {
CGFloat value1 = ((CGFloat)numerator1)/((CGFloat)denominator1);
CGFloat value2 = ((CGFloat)numerator2)/((CGFloat)denominator2);
if (value1 > value2) {
res = NSOrderedDescending;
} else if (value1 < value2) {
res = NSOrderedAscending;
}
}
return res;
}];
//truncate newHighscores
if ([newHighscores count] > 5) {
newHighscores = [newHighscores subarrayWithRange:NSMakeRange(0,5)];
}
[defaults setObject:newHighscores forKey:mode];
} else {
//since score is already in currentHighscores, we're done.
return;
}
}

Related

How to mapping array in YAPdatabase object?

i have test array with objects structure - Group with (NSMutableArray)items, and i save group in YapDatabase
-(void)parseAndSaveJson:(id) json withCompleteBlock:(void(^)())completeBlock{
NSMutableArray *groupsArray = (NSMutableArray *)json;
if (groupsArray != nil) {
YapDatabaseConnection *connectnion = [[DatabaseManager sharedYapDatabase] newConnection];
[connectnion asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (int groupIndex = 0; groupIndex < [groupsArray count]; groupIndex ++) {
LocalGroupsExercise *localGroup = [[LocalGroupsExercise alloc] init];
localGroup.name = groupsArray[groupIndex][LOCAL_GROUPS_NAME];
localGroup.tagColor = groupsArray[groupIndex][LOCAL_GROUPS_TAG_COLOR];
localGroup.idGroup = [groupsArray[groupIndex][LOCAL_GROUPS_ID_GROUP] intValue];
if (groupsArray[groupIndex][LOCAL_GROUPS_EXERCISES] != nil) {
NSMutableArray *exerciseArray = (NSMutableArray *)groupsArray[groupIndex][LOCAL_GROUPS_EXERCISES];
for (int exerciseIndex = 0; exerciseIndex < [exerciseArray count]; exerciseIndex ++) {
LocalExercise *localExercise = [[LocalExercise alloc] init];
localExercise.name = exerciseArray[exerciseIndex][EXERCISE_NAME];
localExercise.exerciseId = [exerciseArray[exerciseIndex][LOCAL_EXERCISE_ID_EXERCISE] intValue];
localExercise.groupId = localGroup.idGroup;
localExercise.type = [exerciseArray[exerciseIndex][EXERCISE_TYPE] intValue];
localExercise.minWeight = [exerciseArray[exerciseIndex][EXERCISE_MIN_WEIGHT] floatValue];
localExercise.maxWeight = [exerciseArray[exerciseIndex][EXERCISE_MAX_WEIGHT] floatValue];
localExercise.minReps = [exerciseArray[exerciseIndex][EXERCISE_MIN_REPS] intValue];
localExercise.maxReps = [exerciseArray[exerciseIndex][EXERCISE_MAX_REPS] intValue];
localExercise.minTimer = [exerciseArray[exerciseIndex][EXERCISE_MIN_TIMER] intValue];
localExercise.maxTimer = [exerciseArray[exerciseIndex][EXERCISE_MAX_TIMER] intValue];
localExercise.timeRelax = [exerciseArray[exerciseIndex][EXERCISE_RELAX_TIME] intValue];
[localGroup.exercises addObject:localExercise];
}
}
[transaction setObject:localGroup forKey:[NSString stringWithFormat:#"%d", localGroup.idGroup] inCollection:LOCAL_GROUPS_CLASS_NAME];
}
YapDatabaseConnection *connectnion = [[DatabaseManager sharedYapDatabase] newConnection];
[connectnion readWithBlock:^(YapDatabaseReadTransaction *transaction) {
LocalGroupsExercise *group = [transaction objectForKey:#"2" inCollection:LOCAL_GROUPS_CLASS_NAME];
NSLog(#"%#", group.name);
NSLog(#"%#", group.tagColor);
NSLog(#"%#", group.exercises);
}];
} completionBlock:^{
completeBlock();
}];
}
}
+ (YapDatabaseView *)setupDatabaseViewForShowGroupsGyms{
YapDatabaseViewGrouping *grouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *(YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) {
if ([object isKindOfClass:[LocalGroupsExercise class]]) {
LocalGroupsExercise *groupExercise = (LocalGroupsExercise *)object;
return [NSString stringWithFormat:#"%#", groupExercise.name];
}
return nil;
}];
YapDatabaseViewSorting *sorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(YapDatabaseReadTransaction *transaction, NSString *group, NSString *collection1, NSString *key1, LocalGroupsExercise *obj1, NSString *collection2, NSString *key2, LocalGroupsExercise *obj2) {
return [obj1.name compare:obj2.name options:NSNumericSearch range:NSMakeRange(0, obj1.name.length)];
}];
YapDatabaseView *databaseView = [[YapDatabaseView alloc] initWithGrouping:grouping sorting:sorting versionTag:#"0"];
return databaseView;
}
[[DatabaseManager sharedYapDatabase] registerExtension:self.databaseGroupView withName:LOCAL_GROUPS_CLASS_NAME];
[_connection beginLongLivedReadTransaction];
self.mappingsGroup = [[YapDatabaseViewMappings alloc] initWithGroupFilterBlock:^BOOL(NSString *group, YapDatabaseReadTransaction *transaction) {
return true;
} sortBlock:^NSComparisonResult(NSString *group1, NSString *group2, YapDatabaseReadTransaction *transaction) {
return [group1 compare:group2];
} view:LOCAL_GROUPS_CLASS_NAME];
[_connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[self.mappingsGroup updateWithTransaction:transaction];
}];
The problem is that the group be NSMutabblArray and I want to see the objects in the table of the array, but [self.mappingsGroup numberOfItemsInSection:section] return only one items in group
You need to configure YapDatabase to use Mantle. By default, it will use NSCoding. (Which is why you're seeing an error about "encodeWithCoder:", as that method is part of NSCoding.)
Take a look at YapDatabase's wiki article entitled "Storing Objects", which talks about how it uses the serializer/deserializer blocks: https://github.com/yaptv/YapDatabase/wiki/Storing-Objects
Basically, when you alloc/init your YapDatabase instance, you'll want to pass a serializer & deserializer block that uses Mantle to perform the serialization/deserialization.
Also, see the various init methods that are available for YapDatabase: https://github.com/yaptv/YapDatabase/blob/master/YapDatabase/YapDatabase.h

array index out of bounds problems

So I'm making a biginteger program, and I'm having a problem with adding two arrays that aren't the same length. The problem I'm having is with the add method. If I'm iterating through an array is there any way to test if element is out of bounds. I've tried testing if the element in a is equal to nil, but I still get the exception. Any help would be great thanks.
#import <Foundation/Foundation.h>
#import "MPInteger.h"
#implementation MPInteger
{
}
-(id) initWithString: (NSString *) x
{
self = [super init];
if (self) {
intString = [NSMutableArray array];
for (int i = 0; i < [x length]; i++) {
NSString *ch = [x substringWithRange:NSMakeRange(i, 1)];
[intString addObject:ch];
}
}
return self;
}
-(NSString *) description
{
return self.description;
}
- (MPInteger *) add: (MPInteger *) x {
NSMutableArray *a = self->intString;
NSMutableArray *b = x->intString;
NSMutableArray *c = [NSMutableArray array];
NSInteger arrayCount;
if (a < b) {
arrayCount = [b count];
} else {
arrayCount = [a count];
}
int num = 10;
int carry = 1;
NSNumber *total;
NSNumber *carrySum;
for (int i = 0; i < arrayCount; i++) {
if (a[i] == nil) {
total = #([b[i] intValue]);
[c addObject:total];
} else if (b[i] == nil) {
total = #([a[i] intValue]);
[c addObject:total];
} else {
total = #([a[i] intValue] + [b[i] intValue]);
[c addObject:total];
}
}
for (NSInteger j = [c count]-1; j >=0; j--) {
if ([c[j] intValue] >= num) {
total = #([c[j] intValue] - num);
carrySum = #([c[j-1] intValue] + carry);
[c replaceObjectAtIndex:j withObject:total];
[c replaceObjectAtIndex:j-1 withObject: carrySum];
}
}
NSString *str = [c componentsJoinedByString:#""];
NSLog(#"%#", str);
return x;
}
-(MPInteger *) multiply: (MPInteger *) x
{
NSMutableArray *a = self->intString;
NSMutableArray *b = x->intString;
NSMutableArray *c = [NSMutableArray array];
NSMutableArray *sum = [NSMutableArray array];
NSNumber *total;
NSNumber *carrySum;
int num = 10;
NSNumber *endZero = 0;
NSInteger bottomCount = [b count]-1;
while (bottomCount != -1) {
for (int i = 0; i < [a count]; i++) {
total = #([a[i] intValue] * [[b objectAtIndex:bottomCount] intValue]);
if (bottomCount == [b count] -1) {
[c addObject:total];
} else {
[c replaceObjectAtIndex:i withObject:total];
}
}
for (NSInteger j = [c count]-1; j>=0; j--) {
NSString *carry = [NSString stringWithFormat:#"%d", [c[j] intValue]];
NSString *carry2 = [carry substringToIndex:1];
int carryFinal = [carry2 intValue];
NSString *carry3 = [carry2 stringByAppendingString:#"0"];
int carry4 = [carry3 intValue];
if ([c[j] intValue] >= num) {
total = #([c[j] intValue] - carry4);
carrySum = #([c[j-1] intValue] + carryFinal);
[c replaceObjectAtIndex:j withObject:total];
[c replaceObjectAtIndex:j-1 withObject: carrySum];
} else {
if(j == 0) {
if (bottomCount == [b count] -1) {
bottomCount = bottomCount - 1;
NSString *str = [c componentsJoinedByString:#""];
[sum addObject: str];
} else {
[c addObject:#([endZero intValue])];
bottomCount = bottomCount - 1;
NSString *str = [c componentsJoinedByString:#""];
[sum addObject: str];
}
}
}
}
}
NSMutableArray *finalSum = [NSMutableArray array];
MPInteger *ele1;
MPInteger *ele2;
MPInteger *eleSum;
NSNumber *endZ= #(0);
[finalSum insertObject:endZ atIndex:0];
for (int k = 0; k < [sum count]; k++) {
NSString *str= [NSString stringWithFormat:#"%d", [sum[k] intValue]];
NSString *str2 = [NSString stringWithFormat:#"%d", [sum[k+1] intValue]];
ele1 = [[MPInteger alloc] initWithString:str];
ele2 = [[MPInteger alloc] initWithString:str2];
eleSum = [ele1 add: ele2];
NSLog(#"%#", eleSum);
}
NSLog(#"%#", sum);
return self;
}
Updated this
for (int i = 0; i < arrayCount; i++) {
if (a[i] == nil) {
total = #([b[i] intValue]);
[c addObject:total];
} else if (b[i] == nil) {
total = #([a[i] intValue]);
[c addObject:total];
} else {
total = #([a[i] intValue] + [b[i] intValue]);
[c addObject:total];
}
}
has now become:
NSMutableArray *c = a.count > b.count ? [a mutableCopy] : [b mutableCopy];
NSArray *shortestArray = a.count > b.count ? b : a;
[shortestArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSNumber *currentNumber, NSUInteger idx, BOOL *stop) {
c[idx] = #(currentNumber.integerValue + [c[idx] integerValue]);
NSLog(#"%#", c[idx]);
}];
What I think I need to do is every index that is in array a and not b or vise versa, is add beginning zeros, but I don't know how to do that.
I printed out what it does after each iteration and it gives:
2013-09-02 12:31:42.630 Asgn1[42471:303] 5
2013-09-02 12:31:42.632 Asgn1[42471:303] 3
2013-09-02 12:31:42.632 Asgn1[42471:303] 1
And a final answer of:
2013-09-02 12:31:42.633 Asgn1[42471:303] 353
For the code that is failing would it not be simpler to take a mutableCopy of the large array and then loop over the smaller array for the calculations?
Perhaps something like this:
NSMutableArray *c = a.count > b.count ? [a mutableCopy] : [b mutableCopy];
NSArray *shortestArray = a.count > b.count ? b : a;
[shortestArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSNumber *currentNumber, NSUInteger idx, BOOL *stop) {
c[idx] = #(currentNumber.integerValue + [c[idx] integerValue]);
}];

Split NSString with multiple delimiters?

For text bozo__foo!!bar.baz, how to split an NSString containing this into (bozo, foo, bar, baz)?
That is, separe it in components with strings (delimiters) __, !! and ..
You can split the strings using NSCharacterSet. Try this
NSString *test=#"bozo__foo!!bar.baz";
NSString *sep = #"_!.";
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:sep];
NSArray *temp=[test componentsSeparatedByCharactersInSet:set];
NSLog(#"temp=%#",temp);
I'm aware that this question has already been answered but this is a way to separate strings using multiple strings. This is a category to NSString.
- (NSArray<NSString *> *)componentsSeparatedByStrings:(NSArray<NSString *> *)separators
{
NSMutableArray<NSString *> *components = [[NSMutableArray alloc] init];
unichar buffer[self.length + 1];
NSInteger currentOrigin = 0;
NSInteger currentLength = 0;
[self getCharacters:buffer];
for(NSInteger i = 0; i < self.length; i++)
{
unichar currentChar = buffer[i];
currentLength++;
for(NSInteger n = 0; n < separators.count; n++)
{
NSString *currentDivider = [separators objectAtIndex:n];
if(currentDivider.length == 0)
{
return #[self];
}
else if(currentDivider.length > 1)
{
BOOL goodMatch = NO;
for(NSInteger x = 0; x < currentDivider.length; x++)
{
unichar charInDivider = [currentDivider characterAtIndex:x];
if(charInDivider == currentChar)
{
goodMatch = YES;
}
else
{
goodMatch = NO;
break;
}
if(goodMatch == YES && ((x + 1) != currentDivider.length))
{
i++;
currentLength++;
currentChar = buffer[i];
}
}
if(goodMatch == YES)
{
NSRange newComponentRange = NSMakeRange(currentOrigin, (currentLength - currentDivider.length));
NSString *newComponent = [self substringWithRange:newComponentRange];
currentOrigin = (i + 1);
currentLength = 0;
[components addObject:newComponent];
NSLog(#"%#", components);
}
}
else // If current divider is only one character long.
{
if([currentDivider characterAtIndex:0] == currentChar)
{
NSRange newComponentRange = NSMakeRange(currentOrigin, (currentLength - 1));
NSString *newComponent = [self substringWithRange:newComponentRange];
currentOrigin = (i + 1);
currentLength = 0;
[components addObject:newComponent];
break;
}
}
}
// Handle the end of the string.
if((i + 1) == self.length)
{
NSRange newComponentRange = NSMakeRange(currentOrigin, currentLength);
NSString *newComponent = [self substringWithRange:newComponentRange];
currentOrigin = 0;
currentLength = 0;
[components addObject:newComponent];
}
}
return components;
}
Example: "ABCD__EFGHI__JKLMNOP-QRST.UV_WXYZ"
NSLog(#"%#", [test componentsSeparatedByStrings:#[#"__", #"-", #"."]]);
Log Result: "(ABCD, EFGHI, JKLMNOP, QRST, "UV_WXYZ")"
NSString *text = #"bozo__foo!!bar.baz";
NSArray *split1 = [text componentsSeparatedByString:#"__"];
NSArray *split2 = [[split1 lastObject] componentsSeparatedByString:#"!!"];
NSArray *split3 = [[split2 lastObject] componentsSeparatedByString:#"."];
NSLog(#"%#, %#, %#, %#", split1[0], split2[0], split3[0], split3[1]);
More functional solution is to apply -componentsSeparatedByString: recursively, for each component, which was derived during previous separator application:
NSString Category
- (NSMutableArray<NSString *> *)gvr_componentsSeparatedByStrings:(NSArray<NSString *> *)separators {
if (separators.count == 0) {
return [NSMutableArray arrayWithObject:self];
}
NSString *separator = [separators firstObject];
NSArray *reducedSeparators = [separators gvr_arrayByRemovingFirstObject];
NSArray *components = [self componentsSeparatedByString:separator];
NSMutableArray *result = [NSMutableArray new];
for (NSString *component in components) {
NSMutableArray *subResult = [component gvr_componentsSeparatedByStrings:reducedSeparators];
[result addObjectsFromArray:subResult];
}
return result;
}
NSArray Category
- (NSArray *)gvr_arrayByRemovingFirstObject {
NSMutableArray *result = [NSMutableArray new];
for (NSInteger i = 1; i < self.count; i++) {
[result addObject:self[i]];
}
return [result copy];
}
I solved it for my project by looking for the longest separator, replacing the others with this one, then do the separation on the only one left.
Try this:
NSString *test = #"bozo__foo!!bar.baz";
test = [test stringByReplacingOccurrencesOfString:#"!!" withString:#"__"];
test = [test stringByReplacingOccurrencesOfString:#"." withString:#"__"];
NSArray<NSString *> *parts = [test componentsSeparatedByString:#"__"];

sortedArrayUsingComparator int

I have an array of a dictionary that it sortedArrayUsingComparator sorts them based on the key;
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSString *first = [a objectForKey:#"perc"];
NSString *second = [b objectForKey:#"perc"];
return [first compare:second];
the problem is that this key is a numbers, and order is so for example: 1,10, 15, 17, 2, 23, etc.
it does not calculate the magnitude of the number.
how can I do?
Can't you return the comparison result like this,
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
int first = [[a objectForKey:#"perc"] intValue];
int second = [[b objectForKey:#"perc"] intValue];
if ( first < second ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( first > second ) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}
A very short way of #(Deepak Danduprolu)'s answer
listPerc = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSNumber *first = #([[a objectForKey:#"perc"] intValue]);
NSNumber *second = #([[b objectForKey:#"perc"] intValue]);
return [first compare:second];
}
Make them into NSNumber objects and compare those instead. Either store them as NSNumbers to start with (preferred method) or convert them when comparing (slower).
Converting an NSString to an NSNumber can be done with:
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:string];
[f release];
Maybe you can do something like this.
- (void)setPlaces:(NSArray *)places
{
if(_places != places)
{
_places = [places sortedArrayUsingComparator:^(id obj1, id obj2){
return [[obj1 objectForKey:FLICKR_PLACE_NAME] caseInsensitiveCompare:[obj2 objectForKey:FLICKR_PLACE_NAME]];
}];
[self.tableView reloadData];
}
}
I did something that was a cross between yours and the above and it works:
- (void)setPlaces:(NSArray *)places
{
if(_places != places)
{
_places = [places sortedArrayUsingComparator:^(id a, id b){
NSString *first = [a objectForKey:FLICKR_PLACE_NAME];
NSString *second = [b objectForKey:FLICKR_PLACE_NAME];
return [first caseInsensitiveCompare:second];
}
];
if (self.tableView.window) [self.tableView reloadData];
} //if _places
} //set

Cocoa Touch - Comparing Ints

I have a maybe simple problem. I am going to generate 3 random numbers ranging from 0 to 2 and I want to determine if there are any duplicates.
Any ideas?
if (num1 == num2) {
}
else if (num1 == num3) {
}
else if (num2 == num3) {
}
else {
//There are no dups.
}
Checks if there is a duplicate.
if (num1 == num2) {
counter++;
}
if (num1 == num3) {
counter++;
}
if (num2 == num3) {
counter++;
}
This finds how many duplicates there are (for an added bonus).
EDIT:
For x amount of numbers you might want to do this (using 10 as my example amount of ints):
int counter = 0;
int i[10] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
for (int g = 0; g < 10; g++)
{
for (int j = g+1; j < 10; j++)
{
if(i[g] == i[j])
{
counter++;
printf(#"%d\n", counter);
//If this if statement is true then there is a dup... In this case none are found.
}
}
}
How about this?
NSArray *randomNumbers = [[NSArray alloc] initWithObjects:#"0",#"1",#"1",#"2",nil];
NSMutableDictionary *occurenceDict = [[NSMutableDictionary alloc] init];
for (NSString *number in randomNumbers)
{
if ([occurenceDict objectForKey:number] == nil) {
[occurenceDict setValue:[NSNumber numberWithInt:[number intValue]] forKey:number];
int occOfNum = 0;
for (int i = 0; i < [randomNumbers count]; i++) {
NSString *currentNumber = [randomNumbers objectAtIndex:i];
if ([currentNumber compare:number] == NSOrderedSame) {
occOfNum++;
}
}
[occurenceDict setValue:[NSNumber numberWithInt:occOfNum] forKey:number];
}
}
for (NSString *key in occurenceDict) {
NSString *occurrences = [occurenceDict objectForKey:key];
NSLog(#"Number %d is contained %d times", [key intValue], [occurrences intValue]);
}
[randomNumbers release];
[occurenceDict release];
Output:
Number 0 is contained 1 times
Number 1 is contained 2 times
Number 2 is contained 1 times
Edit: Incase you want to know how this works, here is the same version but with comments to help you understand it:
// Create array with the numbers that we have randomly generated
NSArray *randomNumbers = [[NSArray alloc] initWithObjects:#"0",#"1",#"1",#"2",nil];
NSMutableDictionary *occurenceDict = [[NSMutableDictionary alloc] init];
for (NSString *number in randomNumbers)
{
// If this number has not been added to the dictionary
if ([occurenceDict objectForKey:number] == nil) {
// Add it
[occurenceDict setValue:[NSNumber numberWithInt:[number intValue]] forKey:number];
// Find how many times the number occurs with the "randomNumbers" array
int occOfNum = 0;
for (int i = 0; i < [randomNumbers count]; i++) {
NSString *currentNumber = [randomNumbers objectAtIndex:i];
if ([currentNumber compare:number] == NSOrderedSame) {
// We found this number at this index, so increment the found count
occOfNum++;
}
}
// Save the number of times which "number" occurs in the dictionary for later
[occurenceDict setValue:[NSNumber numberWithInt:occOfNum] forKey:number];
}
}
// Iterate through all items in the dictionary and print out the result
for (NSString *key in occurenceDict) {
NSString *occurrences = [occurenceDict objectForKey:key];
NSLog(#"Number %d is contained %d", [key intValue], [occurrences intValue]);
}
// Release alloc'ed memory
[randomNumbers release];
[occurenceDict release];
Crikey, these answers are long-winded! Put your random generated numbers into an NSIndexSet. Test the set before inserting a number and you'll know that the number is already present, and so is a dupe.