Encontrar el valor más pequeño y más grande en NSArray de NSNumbers

¿Cuál es una forma efectiva y excelente de comparar todos los valores de NSArray que contiene NSNumbers de floats para encontrar el más grande y el más pequeño?

¿Alguna idea de cómo hacer esto agradable y rápido en objective-c ?

Si la velocidad de ejecución (no la velocidad de progtwigción ) es importante, entonces un ciclo explícito es el más rápido. Hice las siguientes testings con una matriz de 1000000 numbers aleatorios:

Versión 1: ordera la matriz:

 NSArray *sorted1 = [numbers sortedArrayUsingSelector:@selector(compare:)]; // 1.585 seconds 

Versión 2: encoding de valores key, utilizando "doubleValue":

 NSNumber *max=[numbers valueForKeyPath:@"@max.doubleValue"]; NSNumber *min=[numbers valueForKeyPath:@"@min.doubleValue"]; // 0.778 seconds 

Versión 3: encoding de valores key, utilizando "self":

 NSNumber *max=[numbers valueForKeyPath:@"@max.self"]; NSNumber *min=[numbers valueForKeyPath:@"@min.self"]; // 0.390 seconds 

Versión 4: bucle explícito:

 float xmax = -MAXFLOAT; float xmin = MAXFLOAT; for (NSNumber *num in numbers) { float x = num.floatValue; if (x < xmin) xmin = x; if (x > xmax) xmax = x; } // 0.019 seconds 

Versión 5: enumeración de bloques:

 __block float xmax = -MAXFLOAT; __block float xmin = MAXFLOAT; [numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) { float x = num.floatValue; if (x < xmin) xmin = x; if (x > xmax) xmax = x; }]; // 0.024 seconds 

El progtwig de testing crea una matriz de 1000000 numbers aleatorios y luego aplica todas las técnicas de orderación a la misma matriz. Los times anteriores son la salida de una carrera, pero hago unas 20 carreras con resultados muy similares en cada carrera. También cambié el order en que se aplican los 5 methods de sorting para excluir los efectos de caching.

Actualización: ahora he creado un progtwig de testing (con suerte) mejor. El código fuente completo está aquí: https://gist.github.com/anonymous/5356982 . Los times promedio para orderar una matriz de 1000000 numbers aleatorios son (en segundos, en una compilation de lanzamiento iMac Core i5 de 3.1 GHz):

 Ordenar 1.404
 KVO1 1.087
 KVO2 0.367
 Enumeración rápida 0.017
 Bloque enum 0.021

Actualización 2: Como se puede ver, la enumeración rápida es más rápida que la enumeración de bloque (que también se indica aquí: http://blog.bignerdranch.com/2337-incremental-arrayification/ ).

EDITAR: Lo siguiente es completamente incorrecto , porque olvidé inicializar el object utilizado como locking, como Hot Licks notó correctamente, de modo que no se realiza ninguna synchronization. Y con lock = [[NSObject alloc] init]; la enumeración simultánea es tan lenta que no me atrevo a mostrar el resultado. Quizás un mecanismo de synchronization más rápido pueda ayudar …

Esto cambia drásticamente si agrega la opción NSEnumerationConcurrent a la enumeración de bloques:

 __block float xmax = -MAXFLOAT; __block float xmin = MAXFLOAT; id lock; [numbers enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) { float x = num.floatValue; @synchronized(lock) { if (x < xmin) xmin = x; if (x > xmax) xmax = x; } }]; 

El momento aquí es

 Concurrente enum 0.009

por lo que es aproximadamente el doble de rápido que la enumeración rápida. El resultado probablemente no sea representativo porque depende del número de subprocesss disponibles. Pero interesante de todos modos! Tenga en count que he utilizado el método de synchronization "más fácil de usar", que podría no ser el más rápido.

Guarde el flotador envolviéndose en NSNumber luego

 NSNumber *max=[numberArray valueForKeyPath:@"@max.doubleValue"]; NSNumber *min=[numberArray valueForKeyPath:@"@min.doubleValue"]; 

* No comstackdo y marcado, ya marcado con intValue, no estoy seguro sobre el doble o float

ordénalo. Toma el primer y el último elemento.

btw: no puede almacenar flotadores en un NSArray, tendrá que envolverlos en objects NSNumber.

 NSArray *numbers = @[@2.1, @8.1, @5.0, @.3]; numbers = [numbers sortedArrayUsingSelector:@selector(compare:)]; float min = [numbers[0] floatValue]; float max = [[numbers lastObject] floatValue]; 

Estoy de acuerdo con orderar el set y luego elegir los elementos primero y último, pero encuentro que esta solución es más elegante (esto también funcionará para objects no numéricos al cambiar la comparación dentro del bloque):

 NSArray *unsortedArray = @[@(3), @(5), @(1)]; NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { NSNumber *item1 = (NSNumber *)obj1; NSNumber *item2 = (NSNumber *)obj2; return [item1 compare:item2]; }]; 

Si realmente quieres ser elegante y tener una list realmente larga y no quieres bloquear tu hilo principal, esto debería funcionar:

  NSComparator comparison = ^NSComparisonResult(id obj1, id obj2) { NSNumber *item1 = (NSNumber *)obj1; NSNumber *item2 = (NSNumber *)obj2; return [item1 compare:item2]; }; void(^asychSort)(void) = ^ { NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:comparison]; dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"Finished Sorting"); //do your callback here }); }; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), asychSort); 

Hecho simple

 NSArray *numbers = @[@2.1, @8.1, @5.0, @.3]; numbers = [numbers sortedArrayUsingSelector:@selector(compare:)]; float min = [numbers[0] floatValue]; float max = [[numbers lastObject] floatValue]; NSLog(@"MIN%f",min); NSLog(@"MAX%f",max); 

Cualquiera que esté buscando Swift 3.x –

 let arrayOfNumbers = [1.0,4.2,10.3,15.2] print(arrayOfNumbers.max() ?? 0.0)//Max number print(arrayOfNumbers.min() ?? 0.0)//Min number