Pregunta

Lo que quiero hacer parece bastante simple, pero no encuentro 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 implementara Comparable, o usaría Collections.sort con un comparador personalizado en línea... ¿cómo diablos se hace esto en Objective-C?

¿Fue útil?

Solución

método de comparación

O implementas un método de comparación para tu objeto:

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

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

NSSortDescriptor (mejor)

o generalmente incluso mejor:

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

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

Bloques (¡brillantes!)

También existe la posibilidad de ordenar con un bloque desde Mac OS X 10.6 e 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

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

Otros consejos

Consulte el método NSMutableArray sortUsingFunction:context:

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

Los dos objetos son solo instancias de NSOrderedAscending. El tercer objeto es una cadena, p. @ " birthDate " ;.

Esta función devuelve un PersonA.birthDate: Devuelve PersonB.birthDate if NSOrderedDescending < NSOrderedSame. Devolverá <=> if <=> & Gt; <=>. Finalmente, devolverá <=> if <=> == <=>.

Esto es un seudocódigo aproximado; tendrá que desarrollar lo que significa que una fecha sea " less " ;, " more " o " igual " a otra fecha (como comparar segundos desde la é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 desea algo más compacto, puede usar operadores ternarios:

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

La alineación quizás podría acelerar esto un poco, si haces esto mucho.

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 puntos.

También debe decidir qué hacer si los elementos de su matriz no son del tipo correcto, para este ejemplo, acabo de devolver NSOrderedSame, sin embargo, en mi código, hice 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 se ordena en orden descendente.

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

Para este ejemplo en particular, supongo 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: el " ordenado " La matriz se lanzará automáticamente.

Lo intenté todo, pero esto funcionó para mí. En una clase tengo otra clase llamada & Quot; crimeScene & Quot ;, y quiero ordenar por una propiedad de & Quot; <=> & Quot ;.

Esto funciona como un encanto:

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

Falta un paso en Georg Sch & # 246; segunda respuesta de 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];
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 ...

Su Person los objetos necesitan implementar un método, digamos compare: que toma otro Person objeto y retorno NSComparisonResult según la relación entre los 2 objetos.

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

Hay otras formas, pero hasta donde yo sé, no existe ningún equivalente de cacao del Comparable interfaz.Usando sortedArrayUsingSelector: Es probablemente la forma más indolora de hacerlo.

Los bloques iOS 4 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

Para NSMutableArray, use el método sortUsingSelector. Se ordena en el lugar, sin crear una nueva instancia.

Puede usar el siguiente método genérico para su propósito. Debería resolver su problema.

//Called method
-(NSMutableArray*)sortArrayList:(NSMutableArray*)arrDeviceList filterKeyName:(NSString*)sortKeyName ascending:(BOOL)isAscending{
    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:sortKeyName ascending:isAscending];
    [arrDeviceList sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    return arrDeviceList;
}

//Calling method
[self sortArrayList:arrSomeList filterKeyName:@"anything like date,name etc" ascending:YES];

Si solo está ordenando una matriz de NSNumbers, puede ordenarlos con 1 llamada:

[arrayToSort sortUsingSelector: @selector(compare:)];

Eso funciona porque los objetos en la matriz (NSNumber objetos) implementan el método de comparación. Podría hacer lo mismo para NSString objetos, o incluso para una matriz de objetos de datos personalizados que implementan un método de comparación.

Aquí hay un código de ejemplo que usa bloques comparadores. Ordena una serie de diccionarios donde cada diccionario incluye un número en una clave & Quot; sort_key & Quot ;.

#define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
 ^(id obj1, id obj2) 
  {
  NSInteger value1 = [[obj1 objectForKey: SORT_KEY] intValue];
  NSInteger value2 = [[obj2 objectForKey: SORT_KEY] intValue];
  if (value1 > value2) 
{
  return (NSComparisonResult)NSOrderedDescending;
  }

  if (value1 < value2) 
{
  return (NSComparisonResult)NSOrderedAscending;
  }
    return (NSComparisonResult)NSOrderedSame;
 }];

El código anterior pasa por el trabajo de obtener un valor entero para cada clave de clasificación y compararlos, como una ilustración de cómo hacerlo. Dado que los objetos <=> implementan un método de comparación, podría reescribirse mucho más simplemente:

 #define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
^(id obj1, id obj2) 
 {
  NSNumber* key1 = [obj1 objectForKey: SORT_KEY];
  NSNumber* key2 = [obj2 objectForKey: SORT_KEY];
  return [key1 compare: key2];
 }];

o el cuerpo del comparador podría incluso ser destilado a 1 línea:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];

Tiendo a preferir declaraciones simples y muchas variables temporales porque el código es más fácil de leer y más fácil de depurar. El compilador optimiza las variables temporales de todos modos, por lo que no hay ninguna ventaja en la versión todo en una línea.

He creado una pequeña biblioteca de métodos de categoría, llamada Linq to ObjectiveC , que hace este tipo de Lo más fácil. Con el método sort con un selector de teclas, puede ordenar por birthDate de la siguiente manera:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]

Acabo de hacer una clasificación de varios niveles según un requisito personalizado.

// ordenar los valores

    [arrItem sortUsingComparator:^NSComparisonResult (id a, id b){

    ItemDetail * itemA = (ItemDetail*)a;
    ItemDetail* itemB =(ItemDetail*)b;

    //item price are same
    if (itemA.m_price.m_selling== itemB.m_price.m_selling) {

        NSComparisonResult result=  [itemA.m_itemName compare:itemB.m_itemName];

        //if item names are same, then monogramminginfo has to come before the non monograme item
        if (result==NSOrderedSame) {

            if (itemA.m_monogrammingInfo) {
                return NSOrderedAscending;
            }else{
                return NSOrderedDescending;
            }
        }
        return result;
    }

    //asscending order
    return itemA.m_price.m_selling > itemB.m_price.m_selling;
}];

https://sites.google.com/site/greateindiaclub/ mobil-apps / ios / multilevelsortinginiosobjectivec

He usado sortUsingFunction :: en algunos de mis proyectos:

int SortPlays(id a, id b, void* context)
{
    Play* p1 = a;
    Play* p2 = b;
    if (p1.score<p2.score) 
        return NSOrderedDescending;
    else if (p1.score>p2.score) 
        return NSOrderedAscending;
    return NSOrderedSame;
}

...
[validPlays sortUsingFunction:SortPlays context:nil];
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}

Utiliza NSSortDescriptor para ordenar un NSMutableArray con objetos personalizados

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

Ordenar NSMutableArray es muy simple:

NSMutableArray *arrayToFilter =
     [[NSMutableArray arrayWithObjects:@"Photoshop",
                                       @"Flex",
                                       @"AIR",
                                       @"Flash",
                                       @"Acrobat", nil] autorelease];

NSMutableArray *productsToRemove = [[NSMutableArray array] autorelease];

for (NSString *products in arrayToFilter) {
    if (fliterText &&
        [products rangeOfString:fliterText
                        options:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)

        [productsToRemove addObject:products];
}
[arrayToFilter removeObjectsInArray:productsToRemove];

Ordenar usando NSComparator

Si queremos ordenar objetos personalizados, necesitamos proporcionar NSComparator, que se utiliza para comparar objetos personalizados. El bloque devuelve un valor NSComparisonResult para denotar el orden de los dos objetos. Entonces, para ordenar toda la matriz <=> se usa de la siguiente manera.

NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
    return [e1.firstname compare:e2.firstname];    
}];

Ordena usando NSSortDescriptor
Supongamos que & # 8217; s, como ejemplo, que tenemos una matriz que contiene instancias de una clase personalizada, Empleado tiene atributos nombre, apellido y edad. El siguiente ejemplo ilustra cómo crear un NSSortDescriptor que se puede usar para ordenar los contenidos de la matriz en orden ascendente por la clave de edad.

NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Ordenar usando comparaciones personalizadas
Los nombres son cadenas, y cuando ordena las cadenas para presentarlas al usuario, siempre debe usar una comparación localizada. A menudo, también desea realizar una comparación entre mayúsculas y minúsculas. Aquí viene un ejemplo con (localizedStandardCompare :) para ordenar la matriz por apellido y nombre.

NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

Para referencia y discusión detallada, consulte: https://developer.apple.com/library /ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
http: // www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/

Los protocolos y la programación funcional de Swift hacen que sea muy fácil, solo tiene que hacer que su clase se ajuste al protocolo Comparable, implementar los métodos requeridos por el protocolo y luego usar la función ordenada (por:) de orden superior para crear una matriz ordenada sin necesita utilizar matrices mutables por cierto.

class Person: Comparable {
    var birthDate: NSDate?
    let name: String

    init(name: String) {
        self.name = name
    }

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate === rhs.birthDate || lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedSame
    }

    static func <(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedAscending
    }

    static func >(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedDescending
    }

}

let p1 = Person(name: "Sasha")
p1.birthDate = NSDate() 

let p2 = Person(name: "James")
p2.birthDate = NSDate()//he is older by miliseconds

if p1 == p2 {
    print("they are the same") //they are not
}

let persons = [p1, p2]

//sort the array based on who is older
let sortedPersons = persons.sorted(by: {$0 > $1})

//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)

En mi caso, uso " sortedArrayUsingComparator " para ordenar una matriz. Mira el siguiente código.

contactArray = [[NSArray arrayWithArray:[contactSet allObjects]] sortedArrayUsingComparator:^NSComparisonResult(ContactListData *obj1, ContactListData *obj2) {
    NSString *obj1Str = [NSString stringWithFormat:@"%@ %@",obj1.contactName,obj1.contactSurname];
    NSString *obj2Str = [NSString stringWithFormat:@"%@ %@",obj2.contactName,obj2.contactSurname];
    return [obj1Str compare:obj2Str];
}];

También mi objeto es,

@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end

Ordenar matriz en Swift


Para Swifty La persona a continuación es una técnica muy limpia para lograr el objetivo anterior a nivel mundial. Tengamos una clase personalizada de ejemplo de User que tiene algunos atributos.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
}

Ahora tenemos una matriz que debemos clasificar en función de createdDate ya sea ascendente y / o descendente. Así que agreguemos una función para la comparación de fechas.

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
    func checkForOrder(_ otherUser: User, _ order: ComparisonResult) -> Bool {
        if let myCreatedDate = self.createdDate, let othersCreatedDate = otherUser.createdDate {
            //This line will compare both date with the order that has been passed.
            return myCreatedDate.compare(othersCreatedDate) == order
        }
        return false
    }
}

Ahora tengamos un extension de Array para [User]. En palabras simples, agreguemos algunos métodos solo para aquellos Array que solo tienen Array<User> objetos.

extension Array where Element: User {
    //This method only takes an order type. i.e ComparisonResult.orderedAscending
    func sortUserByDate(_ order: ComparisonResult) -> [User] {
        let sortedArray = self.sorted { (user1, user2) -> Bool in
            return user1.checkForOrder(user2, order)
        }
        return sortedArray
    }
}

Uso para orden ascendente

let sortedArray = someArray.sortUserByDate(.orderedAscending)

Uso para orden descendente

let sortedArray = someArray.sortUserByDate(.orderedSame)

Uso para el mismo pedido

<*>
  

El método anterior en <=> solo será accesible si <=> es de tipo   <=> || <=>

Debe crear sortDescriptor y luego puede ordenar el nsmutablearray utilizando sortDescriptor como se muestra a continuación.

 let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
 let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
 print(array)

Úselo así para objetos anidados,

NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];

lastRoute es un objeto y ese objeto contiene el objeto a, que al objeto contiene los valores de cadena de apellido.

NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                 ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;
NSMutableArray *stockHoldingCompanies = [NSMutableArray arrayWithObjects:fortune1stock,fortune2stock,fortune3stock,fortune4stock,fortune5stock,fortune6stock , nil];

NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:@"companyName" ascending:NO];

[stockHoldingCompanies sortUsingDescriptors:[NSArray arrayWithObject:sortOrder]];

NSEnumerator *enumerator = [stockHoldingCompanies objectEnumerator];

ForeignStockHolding *stockHoldingCompany;

NSLog(@"Fortune 6 companies sorted by Company Name");

    while (stockHoldingCompany = [enumerator nextObject]) {
        NSLog(@"===============================");
        NSLog(@"CompanyName:%@",stockHoldingCompany.companyName);
        NSLog(@"Purchase Share Price:%.2f",stockHoldingCompany.purchaseSharePrice);
        NSLog(@"Current Share Price: %.2f",stockHoldingCompany.currentSharePrice);
        NSLog(@"Number of Shares: %i",stockHoldingCompany.numberOfShares);
        NSLog(@"Cost in Dollars: %.2f",[stockHoldingCompany costInDollars]);
        NSLog(@"Value in Dollars : %.2f",[stockHoldingCompany valueInDollars]);
    }
    NSLog(@"===============================");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top