I am trying to sort some list of strings. And names of elements could be almost anything, from real names, strings, dates,... and numbers.
I found NSStringCompareOptions with NSNumericSearch, wich work fast enough and it work nice so :
[1,2,21,3,55,6] --> [1,2,3,6,21,55]
But my problems are negative numbers
[1,2,3,-1,-4,-5] --> [-1,-4,-5,1,2,3]
What it is not right.
I know that Apple stays :
Numeric comparison only applies to the numerals in the string,
not other characters that would have meaning in a numeric representation
such as a negative sign, a comma, or a decimal point.
But my question is how to achieve this, because I know I am not only who do this.
EDIT :
Thanks to Narendra Pandey, but my real case is a little bit complicated, so his answer can't be used here.
So let say I have some dictionary with numbers as keys and strings as values :
dic = {#1:#"123", #2:#"-123", #5:"MyName",...};
then I have array of object with ids.
array = #[{object with id 5}, {object with id 2},...];
and I need sorted array of object by name of properties.
NSStringCompareOptions comparisonOption = NSCaseInsensitiveSearch | NSNumericSearch;
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString * name1 = [dic objectForKey:obj1.someId];
NSString * name2 = [dic objectForKey:obj2.someId];
return [name1 compare:name2 options:comparisonOption];;
}];
EDIT 2:
Maybe I should state that I have solution, but it is 4 times slower that sorting with NSStringCompareOptions
// CHECK IF IT IS NUMBER
NSNumber * number1 = [numberFormatter numberFromString:string1];
NSNumber * number2 = [numberFormatter numberFromString:string2];
//
// NSLog(#"NUMBERS : %#, %#", number1, number2);
if (number1 && number2) {
return [number1 compare:number2];
}
return [string1 compare:string2 options:comparisonOption];
NSArray * numbers = #[#1, #2, #3, #-4, #-5, #6, #9];
NSPredicate * predicateForPositive = [NSPredicate predicateWithFormat:#"integerValue >= 0"];
NSArray * positiveNumbers = [numbers filteredArrayUsingPredicate:predicateForPositive];
NSPredicate * predicateForNegative = [NSPredicate predicateWithFormat:#"integerValue <= 0"];
NSArray * NegativeNumber = [numbers filteredArrayUsingPredicate:predicateForNegative];
NSLog(#"Negative number %#",NegativeNumber);
NSLog(#"Positive number %#",positiveNumbers);
Output:
Negative number (
"-4",
"-5"
)
Positive number (
1,
2,
3,
6,
9
)
Now sort both and concate both Array.
Thanks to Narendra Pandey I found solution for me.
Let me state first that Narendra Pandey solution works, but it is slower, even 3 times.
(in my case 0.014s with NSStringCompareOptions, and 0,042 Narendra Pandey solution).
But if I use his idea and change it a little bit :
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString * name1 = somehow get string1;
NSString * name2 = somehow get string2;
if ([string1 integerValue] <0 && [string2 integerValue]<0) {
return - [string1 compare:string2 options:comparisonOption];
}
return [string1 compare:string2 options:comparisonOption];
}];
this method is faster, in my case 0,015s which is comparable with NSStringCompareOptions.
In this way you avoid to go through whole array at beginning to separate negative and positive numbers, and then sort them.
This function get any array and convert it to sorted Double array
var numberArray: [Any] = [4, 3.9, -23,3, 7.6, -51, 75.3, "0", "-(22)"]
/// This function get any array and convert it to sorted _Double_ array
/// - Note: Non-numerical elements are automatically removed from the array
/// - Parameters:
/// - numbers: array of anything
/// - Returns: sorted _Double_ array
func sortAnyNumbers(_ numbers: [Any]) -> [Double] {
var _numbers: [Double] = []
numbers.forEach { number in
// Delete non-numeric characters
if let numb = Double("\(number)".components(separatedBy: CharacterSet(charactersIn: "-01234567890.").inverted).joined()) {
_numbers.append(numb)
}
}
return _numbers.sorted()
}
print(sortAnyNumbers(numberArray)) //[-51.0, -23.0, -22.0, 0.0, 3.0, 3.9, 4.0, 7.6, 75.3]
I am trying to return the lowest number in an array.
Parameter: arrayOfNumbers - An array of NSNumbers.
Return: The lowest number in the array as an NSInteger.
The code I have thus far doesn't give me any errors, but does not pass the unit tests. What am I doing wrong?
- (NSInteger) lowestNumberInArray:(NSArray *)arrayOfNumbers {
NSNumber* smallest = [arrayOfNumbers valueForKeyPath:#"#min.self"];
for (NSInteger i = 0; i < arrayOfNumbers.count; i++) {
if (arrayOfNumbers[i] < smallest) {
smallest = arrayOfNumbers[i];
}
}
NSInteger smallestValue = [smallest integerValue];
return smallestValue;
}
This is the unit test:
- (void) testThatLowestNumberIsReturned {
NSInteger lowestNumber = [self.handler lowestNumberInArray:#[#3, #8, #-4, #0]];
XCTAssertEqual(lowestNumber, -4, #"Lowest number should be -4.");
lowestNumber = [self.handler lowestNumberInArray:#[#83, #124, #422, #953, #1004, #9532, #-1000]];
XCTAssertEqual(lowestNumber, -1000, #"Lowest number should be -1000.");
}
This method
NSNumber* smallest = [arrayOfNumbers valueForKeyPath:#"#min.self"];
will already determine the smallest number in the array, so the loop inside the method is superfluous (on top of being plain wrong, as #vikingosegundo notices).
you are comparing objects with c types, resulting im pointer addresses being compared with an int.
Beside the fact your smallest is already the smallest, as you used the KVC collection operator #min.self (see Glorfindel answer), the following code shows you correct comparison
if (arrayOfNumbers[i] < smallest)
should be
if ([arrayOfNumbers[i] compare:smallest] == NSOrderingAscending)
or
if ([arrayOfNumbers[i] integerValue] < [smallest integerValue])
I have a randomSpeed variable, and I add those randomSpeeds every few seconds in an mutable array like this ( as object ) .
NSNumber *randomSpeed = [NSNumber numberWithFloat:(arc4random() % 12)-3];
[self.UfoSpeed addObject:randomSpeed];
Now I am getting the index of an Object of another array via
NSUInteger fooIndex = [self.Ufos indexOfObject: ufo];
and now I need the randomSpeed of the index of the Object I got before as a float to add it to another float later on.
For example I need:temp = self.UfoSpeed[fooIndex];
then i add ufo.position.x + temp;
But its not working like that because the randomSpeed is an object of NSNumber and not a float/int.
You can convert NSNumber object to float like this [num floatValue];
How would I get the nearest float in my array to a float of my choice? Here is my array:
[1.20, 1.50, 1.75, 1.95, 2.10]
For example, if my float was 1.60, I would like to produce the float 1.50.
Any ideas? Thanks in advance!
You can do it by sorting the array and finding the nearest one.
For this you can use sortDescriptors and then your algorithm will go.
Even you can loop through, by assuming first as the required value and store the minimum absolute (abs()) difference, if next difference is lesser than hold that value.
The working sample, however you need to handle other conditions like two similar values or your value is just between two value like 2 lies between 1 and 3.
NSArray *array = #[#1.20, #1.50, #1.75, #1.95, #2.10];
double my = 1.7;
NSNumber *nearest = array[0];
double diff = fabs(my - [array[0] doubleValue]);
for (NSNumber *num in array) {
double d = [num doubleValue];
if (diff > fabs(my - d) ) {
nearest = num;
diff = my - d;
}
}
NSLog(#"%#", nearest);
Even though , I execute the below code again & again I get the same Output. But , I think it should not:
int ObjectCount =500;
NSMutableArray *mut_arr = [[NSMutableArray alloc]initWithCapacity:0];
for (int i = 0; i<ObjectCount ; i++)
{
[mut_arr addObject:[NSNumber numberWithInt: rand()%ObjectCount]];
}
NSSet* uniqueSet = [NSSet setWithArray:mut_arr];
NSLog(#"Array of %d objects generates %d Unique Objects",[mut_arr count],[uniqueSet count]);
The output is as follows:
Array of 500 objects generates 317 Unique Objects
Here, Since the array contains random numbers the unique set count should be same again & again for same ObjectCount.
You're not actually generating unique NSNumber objects; some of them are equal.
A NSArray can contain multiple objects that are equal. A NSSet can not. This is why the set created from your array has less objects.
The reason why you're always getting 317 objects is that you're using rand() without seeding: Why do I always get the same sequence of random numbers with rand()?
Consider using arc4random() instead, which is seeding automatically.
Use like this
[mut_arr addObject:[NSNumber numberWithInt:(arc4random() % (ObjectCount-1) + 1)]];