Pregunta ¿Cómo ordeno un NSMutableArray con objetos personalizados en él?


Lo que quiero hacer parece bastante simple, pero no puedo encontrar ninguna respuesta en la web. Yo tengo un NSMutableArray de objetos, y digamos que son objetos de "Persona". Quiero ordenar el NSMutableArray por Person.birthDate que es un NSDate.

Creo que tiene algo que ver con este método:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];

En Java haría que mi objeto implemente Comparable, o use Collections.sort con un comparador personalizado en línea ... ¿cómo diablos haces esto en Objective-C?


1173
2018-04-30 06:10


origen


Respuestas:


Método de comparación

O implementa un método de comparación para su objeto:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor (mejor)

o usualmente aún mejor:

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];

Puede ordenar fácilmente por varias teclas agregando más de una a la matriz. También es posible usar métodos comparadores personalizados. Mira esto la documentación.

Bloques (brillante!)

También existe la posibilidad de ordenar con un bloque desde Mac OS X 10.6 y iOS 4:

NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person*)a birthDate];
    NSDate *second = [(Person*)b birthDate];
    return [first compare:second];
}];

Actuación

los -compare: y los métodos basados ​​en bloques serán bastante más rápidos, en general, que NSSortDescriptor ya que este último depende de KVC. La principal ventaja de la NSSortDescriptor método es que proporciona una forma de definir su orden de clasificación utilizando datos, en lugar de código, lo que hace que sea fácil, p. configurar las cosas para que los usuarios puedan ordenar una NSTableView haciendo clic en la fila del encabezado.


2215
2018-04-30 06:24



Ver el NSMutableArray método sortUsingFunction:context: 

Deberá configurar un comparar función que toma dos objetos (de tipo Person, ya que estás comparando dos Person objetos) y una contexto parámetro.

Los dos objetos son solo ejemplos de Person. El tercer objeto es una cadena, p. @"Fecha de nacimiento".

Esta función devuelve un NSComparisonResult: Vuelve NSOrderedAscending Si PersonA.birthDate < PersonB.birthDate. Regresará NSOrderedDescending Si PersonA.birthDate > PersonB.birthDate. Finalmente, regresará NSOrderedSame Si PersonA.birthDate == PersonB.birthDate.

Este es un pseudocódigo aproximado; necesitará aclarar lo que significa para una fecha ser "menos", "más" o "igual" a otra fecha (como comparar segundos-desde-época, etc.):

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  if ([firstPerson birthDate] < [secondPerson birthDate])
    return NSOrderedAscending;
  else if ([firstPerson birthDate] > [secondPerson birthDate])
    return NSOrderedDescending;
  else 
    return NSOrderedSame;
}

Si quieres algo más compacto, puedes usar operadores ternarios:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}

Inline podría quizás acelerar esto un poco, si haces esto mucho.


103
2018-03-24 22:15



Hice esto en iOS 4 usando un bloque. Tuve que convertir los elementos de mi matriz de id a mi tipo de clase. En este caso, era una clase llamada Score con una propiedad llamada points.

También debe decidir qué hacer si los elementos de su matriz no son del tipo correcto, para este ejemplo acabo de regresar NSOrderedSameSin embargo, en mi código, pensé que era una excepción.

NSArray *sorted = [_scores sortedArrayUsingComparator:^(id obj1, id obj2){
    if ([obj1 isKindOfClass:[Score class]] && [obj2 isKindOfClass:[Score class]]) {
        Score *s1 = obj1;
        Score *s2 = obj2;

        if (s1.points > s2.points) {
            return (NSComparisonResult)NSOrderedAscending;
        } else if (s1.points < s2.points) {
            return (NSComparisonResult)NSOrderedDescending;
        }
    }

    // TODO: default is the same?
    return (NSComparisonResult)NSOrderedSame;
}];

return sorted;

PD: Esto está ordenando en orden descendente.


58
2018-03-16 18:46



A partir de iOS 4 también puede usar bloques para clasificar.

Para este ejemplo en particular, asumo que los objetos en su matriz tienen un método de "posición", que devuelve un NSInteger.

NSArray *arrayToSort = where ever you get the array from... ;
NSComparisonResult (^sortBlock)(id, id) = ^(id obj1, id obj2) 
{
    if ([obj1 position] > [obj2 position]) 
    { 
        return (NSComparisonResult)NSOrderedDescending;
    }
    if ([obj1 position] < [obj2 position]) 
    {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
};
NSArray *sorted = [arrayToSort sortedArrayUsingComparator:sortBlock];

Nota: la matriz "ordenada" se lanzará automáticamente.


28
2017-11-22 10:51



Intenté todo, pero esto funcionó para mí. En una clase tengo otra clase llamada "crimeScene", y desea ordenar por una propiedad de"crimeScene".

Esto funciona como un encanto:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

24
2018-05-06 16:25



Hay un paso faltante en La segunda respuesta de Georg Schölly, pero funciona bien entonces

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];

19
2018-03-30 05:54



NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];

Gracias, está funcionando bien ...


18
2018-04-30 06:20



Tu Person los objetos necesitan implementar un método, digamos compare: que toma otro Person objeto y retorno NSComparisonResult de acuerdo a la relación entre los 2 objetos.

Entonces llamarías sortedArrayUsingSelector: con @selector(compare:) y debería hacerse.

Hay otras formas, pero hasta donde sé, no hay un equivalente de Cocoa Comparable interfaz. Utilizando sortedArrayUsingSelector: es probablemente la forma más indolora de hacerlo.


16
2017-11-09 13:47



iOS 4 bloques te salvarán :)

featuresArray = [[unsortedFeaturesArray sortedArrayUsingComparator: ^(id a, id b)  
{
    DMSeatFeature *first = ( DMSeatFeature* ) a;
    DMSeatFeature *second = ( DMSeatFeature* ) b;

    if ( first.quality == second.quality )
        return NSOrderedSame;
    else
    {
        if ( eSeatQualityGreen  == m_seatQuality || eSeatQualityYellowGreen == m_seatQuality || eSeatQualityDefault  == m_seatQuality )
        {
            if ( first.quality < second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        }
        else // eSeatQualityRed || eSeatQualityYellow
        {
            if ( first.quality > second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        } 
    }
}] retain];

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html un poco de descripción


8
2017-12-06 07:51



por NSMutableArray, utilizar el sortUsingSelector método. Lo ordena, sin crear una nueva instancia.


7
2018-04-16 09:17