NSArray Sorting Floats - sorting

I try hard not to ask questions willy nilly but this is killing me wasted a good couple of hours on it, now so i give in and hope someone can help me please.
I simply can't order my NSArray containing floats correctly the closest i can get is this.
Populating the Array with the following
[combosMasterLength addObject:[NSString stringWithFormat:#"%0.1f", finalL]];
NSMutableSet* set1 = [NSMutableSet setWithArray:combosMasterLength];//Array Final Length
NSMutableSet* set2 = [NSMutableSet setWithArray:CombosMasterWidth];//Array Final Width
[set1 intersectSet:set2]; //this will give me only the objects that are in both sets
NSArray *combos = [set1 allObjects];//cread my combos array with the unique values
NSArray* sortedArray = [combos sortedArrayUsingSelector:#selector(compare:)];//my sorting statment not quite working
NSLog(#"SORTED ARRAY %#",sortedArray);
What i get from my output is : -
"1.2",
"1.5",
"12.0",
"2.0",
"2.4",
"3.0",
"4.0",
"6.0"
I know what the issue is i just simply have no idea how to sort it and i tried the reference apple docs but they have now gone way over my head, Anyone have any ideas as to how i would sort this so it returns
"1.2",
"1.5",
"2.0",
"2.4",
"3.0",
"4.0",
"6.0"'
"12.0"
Thanks In Advance
Mr H

Since you seem to be wanting to sort NSString by numeric value, you'll need to convert the values to a numeric value and compare with that. You can do that, for example, using sortedArrayUsingComparator;
NSArray *myArray = [NSArray arrayWithObjects:
#"1.2", #"1.5", #"12.0", #"2.0",
#"2.4", #"3.0", #"4.0", #"6.0", nil];
NSArray *sorted = [myArray sortedArrayUsingComparator:
^NSComparisonResult(NSString *obj1, NSString *obj2)
{
if ([obj1 doubleValue] > [obj2 doubleValue])
return NSOrderedDescending;
else if ([obj1 doubleValue] < [obj2 doubleValue])
return NSOrderedAscending;
return NSOrderedSame;
}
];
NSLog(#"%#", sorted);
> (
"1.2",
"1.5",
"2.0",
"2.4",
"3.0",
"4.0",
"6.0",
"12.0"
)

Related

NSTextView + NSTextFinder + my separate NSSearchField

I am trying to implement searching in NSTextView with search query coming from my custom NSSearchField.
Sounds pretty simple, but I cannot get it working.
So far I've looked through all the Apple Documentation about NSTextFinder, its client and FindBarContainer. The TextFinder simply provides the FindBarView to the container, and container shows it when you activate searching.
All the communication between the client, container and TextFinder is hidden. It just looks like a black-box that is designed to work "as is" without any customisation or interference.
But what about - (void)performAction:(NSTextFinderAction)op method of NSTextFinder? Isn't it for sending custom commands to the TextFinder?
I was trying to assign a new search string to it with the following:
NSPasteboard* pBoard = [NSPasteboard pasteboardWithName:NSFindPboard];
[pBoard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, NSPasteboardTypeTextFinderOptions, nil] owner:nil];
[pBoard setString:_theView.searchField.stringValue forType:NSStringPboardType];
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSTextFinderCaseInsensitiveKey,
[NSNumber numberWithInteger:NSTextFinderMatchingTypeContains], NSTextFinderMatchingTypeKey,
nil];
[pBoard setPropertyList:options forType:NSPasteboardTypeTextFinderOptions];
[textFinder performAction:NSTextFinderActionSetSearchString];
but that doesn't work and simply breaks the normal findBar operation.
I have a strong feeling that I am doing something wrong.
All I want is to have a standard search functionality in my own NSSearchField. Is that possible?
I bet I am not the first one who is not happy with normal findBar.
Your help is very needed and appreciated!
For an NSTextView, NSTextFinder is mostly just a user interface for NSString's func range(of searchString: String, options mask: NSString.CompareOptions = [], range rangeOfReceiverToSearch: NSRange) -> NSRange
If you want to implement your own search on an NSTextView, use that. To search forward, you construct a range starting at the end of the current selections's range and going to the end of the NSTextView's text. To search backward, construct a range starting at 0 and going to the beginning of the current selection's range, and tell NSString to use backwards search.
If NSString returns a .notFound range, implement wrap-around yourself.
If you need startsWith, endsWith or wholeWord you'll need to take the result NSString's func gives you, check to see if it will do, and if not adjust the range and call it again.
You can use NSComboBox. Return search value using below delegate:
- (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)substring
{
if ([aComboBox tag] == 101 || [aComboBox tag] == 102) {
NSArray *currentList;
if ([aComboBox tag] == 101) {
NSArray *keyArray = keySuggestions;
currentList = keyArray;
} else {
currentList = [NSArray arrayWithArray:self.valueSuggestions];
}
NSEnumerator *theEnum = [currentList objectEnumerator];
id eachString;
NSInteger maxLength = 0;
NSString *bestMatch = #"";
while (nil != (eachString = [theEnum nextObject])) {
NSString *commonPrefix = [eachString
commonPrefixWithString:substring options:NSCaseInsensitiveSearch];
if ([commonPrefix length] >= [substring length] && [commonPrefix
length] > maxLength)
{
maxLength = [commonPrefix length];
bestMatch = eachString;
break;
}
}
return bestMatch;
}
return substring;
}

NSArray Sorting

I have an NSArray with values that I am pulling from an NSDictionary using a selector to sort with which has the following values:
John
Brian
Alex
....
Dave
When I use the code below, since they are being compared as strings, the list comes back with:
NSArray *array = [[[self myDictionary] allValues] sortedArrayUsingSelector:#selector(compare:)];
John
Dave
Brian
...
How can I get these values to sort correctly where they are in order 1, 2, 3, etc.? I've looked at several different examples for sorting, but have not been able to find an example like mine. I must also mention that I'm new to objective-c and iOS. Any help would be greatly appreciated.
Thanks!
I was actually able to figure out the solution. I created an NSComparisonResult block using custom logic to read the number portion off of the front of each string and then comparing them numerically:
NSComparisonResult (^sortByNumber)(id, id) = ^(id obj1, id obj2)
{
//Convert items to strings
NSString *s1 = (NSString *)obj1;
NSString *s2 = (NSString *)obj2;
//Find the period and grab the number
NSUInteger periodLoc1 = [s1 rangeOfString:#"."].location;
NSString *number1 = [s1 substringWithRange:NSMakeRange(0, periodLoc1)];
NSUInteger periodLoc2 = [s2 rangeOfString:#"."].location;
NSString *number2 = [s2 substringWithRange:NSMakeRange(0, periodLoc2)];
//Compare the numeric values of the numbers
return [number1 compare:number2 options:NSNumericSearch];
};
Then I sort my array by calling:
NSArray *array = [[[self myDictionary] allValues] sortedArrayUsingComparator:sortByNumber];

Correct way to declare an NSArray (Memory-Wise)

I have an NSArray which has the properties nonatomic and retain. What I would like to know is am I initialising it in the correct way so that the retain count is what it should be?
The _lengthArr is the #synthesized ivar of lengthArr in my.h
#synthesize lengthArr = _lengthArr;
_lengthArr = [[NSArray arrayWithObjects:#"10 Minutes", #"20 Minutes", #"30 Minutes", #"1 Hour", #"2 Hours", #"5 Hours", #"5 Hours +", nil] retain];
You are assigning to an ivar there, so the code you posted will do what you want. You could also use _lengthArr = [[NSArray alloc] initWithObjects:...], which would avoid a useless autorelease.
Or you could do self.lengthArr = [NSArray arrayWithObjects:...] to allow the setter to automatically retain it, although it is recommended to avoid this in initialization methods.

Check for String within a String

I'm trying to compare two strings
NSString strOne = #"Cat, Dog, Cow";
NSString strTwo = #"Cow";
How do I determine if strOne contains strTwo
Try using rangeOfString:
NSRange result = [strOne rangeOfString:strTwo];
From the documentation:
Returns an NSRange structure giving the location and length in the receiver of the first occurrence of aString. Returns {NSNotFound, 0} if aString is not found or is empty (#"").
For anyone needing the code to check is a string exists within a string, here's my code thanks to fbrereto. This example checks to see if any string contained in an array of strings (stringArray) can be found within a string (myString):
int count = [stringArray count];
for (NSUInteger x = 0; x < count; ++x) {
NSRange range = [self.myString rangeOfString:[stringArray objectAtIndex:x]];
if (range.length > 0) {
// A match has been found
NSLog(#"string match: %#",[stringArray objectAtIndex:x]);
}
}
I believe this is the correct syntax for checking if the range exists (correcting response from Kendall):
range.location != NSNotFound
Gradually straying off topic, but I always explode my strings, which would mean just exploding it using your search string as a key and you can use the array count to see how many instances you have.
Just incase anyone is coming from a code language that uses "explode" to blow a string up into an array like me, I found writing my own explode function tremendously helpful, those not using "explode" are missing out:
- (NSMutableArray *) explodeString : (NSString *)myString key:(NSString*) myKey
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSRange nextBreak = [myString rangeOfString:myKey];
while(nextBreak.location != NSNotFound)
{
[myArray addObject: [myString substringToIndex:nextBreak.location]];
myString = [myString substringFromIndex:nextBreak.location + nextBreak.length];
nextBreak = [myString rangeOfString:myKey];
}
if(myString.length > 0)
[myArray addObject:myString];
return myArray;
}
works like this:
[self explodeString: #"John Smith|Age: 37|Account Balance: $75.00" key:#"|"];
which will return this array:
[#"John Smith", #"Age: 37", #"Account Balance: $75.00"];
This lets you quickly pull out a specific value in a tight space, Like if you have a client and you want to know how much money he has:
[[self explodeString: clientData key: pipe] objectAtIndex: 1];
or if you wanted specifically the dollar amount as a float:
[[[self explodeString: [[self explodeString: clientData key: pipe] objectAtIndex: 1] key: #": "] objectAtIndex: 2] floatValue];
anyway I find arrays way easier to work with and more flexible, so this is very helpful to me. Additionally with a little effort you could make an "explodable string" data type for your private library that lets you treat it like a string or return an index value based on the key
ExplodableString *myExplodableString;
myExplodableString.string = #"This is an explodable|string";
NSString *secondValue = [myExplodableString useKey: #"|" toGetValue: index];

Renaming keys in NSMutableDictionary

Given an NSMutableDictionary *dict, is this a bad way to replace keys with a new name? Is there an easier way?
NSArray *originalField = [NSArray arrayWithObjects:#"oldkey", #"oldkey2", nil];
NSArray *replacedField = [NSArray arrayWithObjects:#"newkey", #"newkey2", nil];
for (int i=0; i<[originalField count]; ++i)
{
if ([dict objectForKey:[originalField objectAtIndex:i]] != nil) {
[dict setObject:[dict objectForKey:[originalField objectAtIndex:i]] forKey:[replacedField objectAtIndex:i]];
[dict removeObjectForKey:[originalField objectAtIndex:i]];
}
}
Thanks!
Nope, that's pretty much it. In general, you'd use fast enumeration and/or NSEnumerator to walk the arrays instead of going index-by-index, but since you're walking two parallel arrays, indexes are the clearest way to do it.
That's not a bad way per se, but you could certainly make it more elegant (and in my opinion, easier) by cleaning up the code a bit and eliminating a few redundant method calls. As #Peter suggested, fast enumeration (you can use it on Leopard+ or iPhone) would be much quicker and cleaner, and therefore generally preferable. Here's an example:
NSArray *originalField = [NSArray arrayWithObjects:#"oldkey", #"oldkey2", nil];
NSArray *replacedField = [NSArray arrayWithObjects:#"newkey", #"newkey2", nil];
id anObject;
NSEnumerator *replacementKeys = [replacedField objectEnumerator];
for (id originalKey in originalField) {
if ((anObject = [dict objectForKey:originalKey]) != nil) {
[dict removeObjectForKey:originalKey];
[dict setObject:anObject forKey:[replacementKeys nextObject]];
}
}
One note of warning: you'll want to make sure that the arrays originalField and replacedField are the same length. If the latter is shorter, you'll get an exception, either from -[NSEnumerator nextObject] or -[NSArray objectAtIndex:]. If the latter is longer, you may wonder why some of the replacement keys are never used. You could use an NSAssert macro to verify that during debugging, and it will be disabled automatically in release builds.
Alternatively, if there is truly a one-to-one relationship between the keys, perhaps you could use a dictionary to map from old key to new key, and enumerate over the result of -[NSDictionary allKeys].

Resources