sorting NSArray of NSStrings by match occurrence - cocoa

How can I sort NSArray by search pattern matching?
So if for example I have a search pattern equal 'xd' and an array of values:
axd
bxd
xdd
gtxd
xdc
how can I get the output like below:
xdc
xdd
axd
bxd
gtxd
Thank you in advance.

Use NSArray's sortedArrayUsingFunction: with a function that orders first by the position of the search term, then by the strings' natural ordering.
NSInteger sorter(id arg1, id arg2, void *context)
{
NSString *searchTerm = (NSString *)context;
NSRange range1 = [arg1 rangeOfString:searchTerm];
NSRange range2 = [arg2 rangeOfString:searchTerm];
if (range1.location < range2.location)
return NSOrderedAscending;
if (range1.location > range2.location)
return NSOrderedDescending;
return [arg1 compare:arg2];
}
NSArray *array = [NSArray arrayWithObjects:#"axd", #"bxd", #"xdd", #"gtxd", #"xdc", nil];
NSArray *sortedArray = [array sortedArrayUsingFunction:sorter context:#"xd"];
This prints:
2011-02-15 01:33:49.642 GreatApp[78849:a0f] (
xdc,
xdd,
axd,
bxd,
gtxd
)

You can use NSArray's sortedArrayUsingFunction:context: method:
NSInteger occurenceSort(NSString* s1, NSString* s2, void *context)
{
NSRange range1 = [s1 rangeOfString:(NSString*)context];
NSRange range2 = [s2 rangeOfString:(NSString*)context];
if (range1.location < range2.location)
return NSOrderedAscending;
else if (range1.location > range2.location)
return NSOrderedDescending;
return NSOrderedSame;
}
...
NSString *stringToSearch = #"xd";
NSArray *sorterArray = [yourArray sortedArrayUsingFunction:occurenceSort context:stringToSearch];

Related

Extract numbers from NSString

How can I extract numbers from a string defined like:
NSString *getNumber = #"Price138.50 Code112.250"
I need to extract both 138.50 and 112.25 from this string.
Use NSScanner, slightly modified from this Apple example:
NSCharacterSet *numberCharset = [NSCharacterSet characterSetWithCharactersInString:#"0123456789-"];
NSScanner *theScanner = [NSScanner scannerWithString:aString];
while (![theScanner isAtEnd]) {
// Eat non-digits and negative sign
[theScanner scanUpToCharactersFromSet:numberCharset
intoString:NULL];
float aFloat;
if ([theScanner scanFloat:&aFloat]) {
NSLog(#"Found %f", aFloat);
}
}
NSString *getNumber = #"Price138.50 Code112.250";
getNumber = [getNumber stringByReplacingOccurrencesOfString:#"Price" withString:#""];
getNumber = [getNumber stringByReplacingOccurrencesOfString:#"Code" withString:#""];
getNumber = [getNumber stringByReplacingOccurrencesOfString:#" " withString:#","];
NSArray *values = [getNumber componentsSeparatedByString:#","];
NSLog(#"Price Value: %#", [values firstObject]);
NSLog(#"Code Value: %#", [values lastObject]);
If your string will be of same kind then this can be a possible way. Not Recommended.

Sort by Double Value and not String Value

I'm currently pulling info from an sql DB where the 'cachedDist' column is set as a double. However when I pull it into my app and create my array I turn it into an String and the sort will obviously be off, 18.15 will come before 2.15. How do I fix that in my code so it will sort distance as a Double and not a String?
In Bar object.
NSString *cachedDist
#property(nonatomic,copy) NSString *cachedDist;
#synthesize cachedDist;
My while loop in the View Controller.
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
Bar * bar = [[Bar alloc] init];
bar.barName = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
bar.barAddress = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,2)];
bar.barCity = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 3)];
bar.barState = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 4)];
bar.barZip = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 5)];
bar.barLat = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 8)];
bar.barLong = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 9)];
if (currentLoc == nil) {
NSLog(#"current location is nil %#", currentLoc);
}else{
CLLocation *barLocation = [[CLLocation alloc] initWithLatitude:[bar.barLat doubleValue] longitude:[bar.barLong doubleValue]];
bar.cachedDist = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];
[thebars addObject:bar];
}
My sorting
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"cachedDist" ascending:YES];
sortedArray = [thebars sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
return sortedArray;
NSString has a method doubleValue to make this quite simple:
double cachedDistance = [cachedDistanceString doubleValue];
which you can use in a custom comparator for your sorting, or else make the property an NSNumber or double to make sorting that much easier. (I'm not sure how you are sorting...)
edit:
I re-evaluated your code, and now it looks like we are going from a double to a string to a double... we can cut out the middle-man, so to speak.
In your #prototype section, change the #property:
// #property(nonatomic,copy) NSString *cachedDist; // old way
#property(nonatomic) double cachedDist;
then assign it like this:
bar.cachedDistance = [currentLoc distanceFromLocation: barLocation]/1000;
and remove the lines which create a string from the distance (which is actually just a double).
Alternatively, if you want to be more object oriented, you can (should?) use NSNumber objects:
#property(nonatomic,copy) NSNumber *cachedDist;
...
bar.cachedDistance = [NSNumber numberWithDouble:[currentLoc distanceFromLocation: barLocation]/1000];

How do i get the range of the current paragraph in a NSTextView where the cussor stayed there?

Now i need to change the alignment of a paragraph in a nstextview without select it ,so i need to know the range of the current range of the paragraph?
I have a subclass of NSTextView so you need to access textStorage and selectedRange different than [self textStorage] and [self selectedRange].
NSTextStorage *textStorage = [self textStorage];
NSString *string = [textStorage string];
NSUInteger editEnd = [self selectedRange].location;
NSUInteger editStart = editEnd-[textStorage editedRange].length;
NSUInteger maxLength = [string length];
while (editStart > 0) {
unichar theChr = [string characterAtIndex:editStart-1];
if( theChr == '\n' || theChr == '\r' ) {
break;
}
--editStart;
}
while (editEnd < maxLength) {
unichar theChr = [string characterAtIndex:editEnd];
if( theChr == '\n' || theChr == '\r' ) {
break;
}
++editEnd;
}
NSRange paragraphRange = NSMakeRange(editStart, editEnd-editStart);
Here's a shortcut:
NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange: [textView selectedRange]];
First, get the range where the cursor stayed through [textView selectedRange]
Then you can get the line range through - (NSRange)lineRangeForRange:(NSRange)range of [textView string]
Here is a example code:
NSRange sel = [textView selectedRange];
NSString *viewContent = [textView string];
NSRange lineRange = [viewContent lineRangeForRange:NSMakeRange(sel.location,0)];
detail in there.
How to get the selected line range of NSTextView?

Problem with NSNumber and plotting a graph with Core-Plot

I try to plot a Bar Chart with Core-Plot with an Array (content are NSIntegers) given one view before.
After transfering the Array in an NSInteger, i must convert it into a NSDecimalNumber, and in this process, my NSInteger (for example 45) becomes "60900224"...
Here's the code extract:
-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{ NSInteger *values = [Werte objectAtIndex:index];
NSDecimalNumber *num = nil;
if ( [plot isKindOfClass:[CPBarPlot class]] ) {
switch ( fieldEnum ) {
case CPBarPlotFieldBarLocation:
num = (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:index];
break;
case CPBarPlotFieldBarLength:
//num = (NSDecimalNumber *)[NSDecimalNumber numberWithUnsignedInteger:(index+1)*(index+1)];
num = [NSNumber numberWithInt:values];
if ( [plot.identifier isEqual:#"Bar Plot 2"] )
num = [num decimalNumberBySubtracting:[NSDecimalNumber decimalNumberWithString:#"10"]];
break;
}
}
return num;
}
Thanks for help!!
NSInteger is not an object type and can't be stored in an NSArray (which your Werte appears to be). You seem to be implicitly converting from a pointer to an integer.
Instead, you should always put NSNumber objects into the array, and then get NSInteger values out of those via integerValue:
NSInteger value = [[Werte objectAtIndex:index] integerValue];

Cocoa - Trim all leading whitespace from NSString

(have searched, but not been able to find a simple solution to this one either here, or in Cocoa docs)
Q. How can I trim all leading whitespace only from an NSString? (i.e. leaving any other whitespace intact.)
Unfortunately, for my purposes, NSString's stringByTrimmingCharactersInSet method works on both leading and trailing.
Mac OS X 10.4 compatibility needed, manual GC.
This creates an NSString category to do what you need. With this, you can call NSString *newString = [mystring stringByTrimmingLeadingWhitespace]; to get a copy minus leading whitespace. (Code is untested, may require some minor debugging.)
#interface NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace;
#end
#implementation NSString (trimLeadingWhitespace)
-(NSString*)stringByTrimmingLeadingWhitespace {
NSInteger i = 0;
while ((i < [self length])
&& [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[self characterAtIndex:i]]) {
i++;
}
return [self substringFromIndex:i];
}
#end
This is another solution using Regular Expressions (requires iOS 3.2):
NSRange range = [string rangeOfString:#"^\\s*" options:NSRegularExpressionSearch];
NSString *result = [string stringByReplacingCharactersInRange:range withString:#""];
And if you want to trim the trailing whitespaces only you can use #"\\s*$" instead.
This code is taking blanks.
NSString *trimmedText = [strResult stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#",trimmedText);
Here is a very efficient (uses CoreFoundation) way of doing it (Taken from kissxml):
- (NSString *)trimWhitespace {
NSMutableString *mStr = [self mutableCopy];
CFStringTrimWhitespace((CFMutableStringRef)mStr);
NSString *result = [mStr copy];
[mStr release];
return [result autorelease];
}
NSString *myText = #" foo ";
NSString *trimmedText = [myText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSLog(#"old = [%#], trimmed = [%#]", myText, trimmedText);
Here's what I would do, and it doesn't involve categories!
NSString* outputString = inputString;
NSRange range = [inputString rangeOfCharacterFromSet: [NSCharacterSet whitespaceCharacterSet]
options:0];
if (range.location == 0)
outputString = [inputString substringFromIndex: range.location + range.length];
This is much less code.
I didn't really have much time to test this, and I'm not sure if 10.4 contains the UTF8String method for NSString, but here's how I'd do it:
NSString+Trimming.h
#import <Foundation/Foundation.h>
#interface NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront;
#end
NSString+Trimming.m
#import "NSString+Trimming.h"
#implementation NSString (Trimming)
-(NSString *) stringByTrimmingWhitespaceFromFront
{
const char *cStringValue = [self UTF8String];
int i;
for (i = 0; cStringValue[i] != '\0' && isspace(cStringValue[i]); i++);
return [self substringFromIndex:i];
}
#end
It may not be the most efficient way of doing this but it should work.
str = [str stringByReplacingOccurrencesOfString:#" " withString:#""];

Resources