ios - كيف يمكنني فرز NSMutableArray مع كائنات مخصصة في ذلك؟




objective-c sorting cocoa-touch (21)

ما أريد فعله يبدو بسيطًا جدًا ، ولكن لا يمكنني العثور على أي إجابات على الويب. لدي NSMutableArray من الكائنات ، ودعونا نقول أنها كائنات "شخص". أريد فرز NSMutableArray بواسطة Person.birthDate وهو NSDate.

أعتقد أن لها علاقة بهذه الطريقة:

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

في Java ، أجعل الكائن تنفيذًا قابل للمقارنة ، أو استخدم Collections.sort مع مقارنة مخصصة مضمنة ... كيف تقوم بذلك على الأرض في Objective-C؟


Answers

أنا فعلت هذا في iOS 4 باستخدام كتلة. كان عليه أن يلقي عناصر صفيفي من معرف إلى نوع الفصل. في هذه الحالة ، كانت فئة تسمى درجة مع خاصية تسمى النقاط.

كما تحتاج إلى تحديد ما يجب فعله إذا لم تكن عناصر الصفيف الخاص بك هي النوع الصحيح ، لهذا المثال عدت للتو NSOrderedSame ، ومع ذلك في رمز بلدي أنا استثناء.

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;

ملاحظة: هذا هو فرز بترتيب تنازلي.


في حالتي ، أستخدم "sortedArrayUsing COMparator" لتصنيف مصفوفة. انظر إلى الرمز أدناه.

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];
}];

أيضا موضوعي هو ،

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

يمكنك استخدام الطريقة العامة التالية لغرضك. يجب أن تحل مشكلتك.

//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];

يجب عليك إنشاء sortDescriptor ومن ثم يمكنك فرز nsmutablearray باستخدام sortDescriptor مثل أدناه.

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

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

شكرا ، انها تعمل بشكل جيد ...


مقارنة الطريقة

إما أن تقوم بتطبيق طريقة مقارنة لكائنك:

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

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

NSSortDescriptor (أفضل)

أو عادة أفضل:

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

يمكنك بسهولة فرز مفاتيح متعددة عن طريق إضافة أكثر من واحد إلى الصفيف. باستخدام أساليب المقارنة المخصصة ممكن أيضا. إلقاء نظرة على الوثائق .

كتل (لامعة!)

هناك أيضًا إمكانية للفرز باستخدام كتلة منذ نظام التشغيل Mac OS X 10.6 و 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];
}];

أداء

سيكون -compare: والأساليب المستندة إلى كتلة أسرع قليلاً بشكل عام ، من استخدام NSSortDescriptor لأن الأخير يعتمد على KVC. تتمثل الميزة الأساسية لطريقة NSSortDescriptor أنها توفر طريقة لتحديد ترتيب الفرز باستخدام البيانات ، بدلاً من التعليمة البرمجية ، مما يجعل من السهل إعداد العناصر بحيث يمكن للمستخدمين فرز NSTableView بالنقر فوق صف الرأس.


بدءًا من iOS 4 ، يمكنك أيضًا استخدام كتل للفرز.

لهذا المثال بالذات أفترض أن الكائنات الموجودة في الصفيف لديك لها طريقة "position" ، والتي تقوم بإرجاع 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];

ملحوظة: سيتم تجهيز المصفوفة "المصنفة" تلقائيًا.


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

فرز NSMutableArray بسيط جدا:

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];

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(@"===============================");

إذا كنت تقوم فقط بفرز مصفوفة من NSNumbers ، فيمكنك فرزها باستخدام مكالمة واحدة:

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

يعمل ذلك لأن الكائنات الموجودة في الصفيف (كائنات NSNumber ) تقوم بتطبيق أسلوب المقارنة. يمكنك القيام بنفس الشيء بالنسبة لكائنات NSString ، أو حتى لصفيف من كائنات البيانات المخصصة التي تقوم بتطبيق طريقة مقارنة.

إليك بعض الأمثلة على التعليمات البرمجية باستخدام مجموعات المقارنة. يفرز صفيف من القواميس حيث يتضمن كل قاموس رقم في مفتاح "sort_key".

#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;
 }];

يمر الرمز أعلاه من خلال عمل الحصول على قيمة عددية لكل مفتاح فرز ومقارنتها ، كتوضيح لكيفية القيام بذلك. نظرًا لتطبيق كائنات NSNumber طريقة مقارنة ، فيمكن إعادة كتابتها ببساطة أكثر:

 #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];
 }];

أو حتى يمكن تقريب جسم المقارنة إلى سطر واحد:

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

أميل إلى تفضيل عبارات بسيطة والكثير من المتغيرات المؤقتة لأن التعليمة البرمجية أسهل في القراءة ، وأسهل في التصحيح. يقوم المترجم بتحسين المتغيرات المؤقتة على أي حال ، لذلك لا توجد ميزة للإصدار المتكامل في سطر واحد.


هناك خطوة مفقودة في إجابة جورج شولي الثانية ، لكنها تعمل بشكل جيد في ذلك الوقت.

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

يمكنك فرز مجموعة من الكائنات المخصصة باستخدام الفرز السريع. يرجى الاطلاع على المثال أدناه باستخدام Swift 4

func sortArrayOfPersonInAscendingOrder() {
        if self.arrSortingPersons != nil, self.arrSortingPersons.count > 0 {
            self.arrSortingPersons.sort(by: { (person1, person2) -> Bool in
                return person1.birthDate < person2.birthDate
            })
        }

        print("Sorted Array In Ascending Order:\n\(self.arrSortingPersons)")
    }

راجع أسلوب طريقة sortUsingFunction:context:

ستحتاج إلى إعداد وظيفة مقارنة تأخذ شيئين (من نوع Person ، بما أنك تقارن بين شخصين) ومعلمة سياق .

والكائنات هما مجرد أمثلة Person . الكائن الثالث عبارة عن سلسلة ، على سبيل المثال @ "birthdate".

ترجع هذه الدالة NSComparisonResult : تقوم بإرجاع NSOrderedAscending إذا كان PersonA.birthDate < PersonB.birthDate . سيعود NSOrderedDescending حالة PersonA.birthDate > PersonB.birthDate . وأخيرًا ، سيعود NSOrderedSame إذا كان PersonA.birthDate == PersonB.birthDate .

هذا هو pseudocode الخام. ستحتاج إلى توضيح ما يعنيه لتاريخ واحد ليكون "أقل" ، "أكثر" أو "مساوٍ" لتاريخ آخر (مثل مقارنة الثواني - منذ - حقبة إلخ.):

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;
}

إذا كنت تريد شيئًا أكثر تعقيدًا ، فيمكنك استخدام عوامل التشغيل الثلاثية:

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

ربما يمكن أن يسرع هذا الأمر قليلاً ، إذا قمت بذلك كثيرًا.


لقد جربت كل شيء ، لكن هذا عمل بالنسبة لي. في الفصل الدراسي لديّ فصل آخر اسمه " crimeScene " ، وأريد الفرز حسب خاصية " crimeScene ".

هذا يعمل مثل السحر:

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

إن بروتوكولات Swift والبرمجة الوظيفية تجعل من السهل عليك فقط جعل صفك مطابقاً للبروتوكول المقارن ، وتنفيذ الطرق التي يتطلبها البروتوكول ثم استخدام الدالة المرتبة عالية الجودة (بواسطة:) لإنشاء مصفوفة مرتبة بدون الحاجة إلى استخدام صفائف قابلة للتغيير من جانب الطريق.

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)

ترتيب المصفوفة في سويفت

ل Swifty Person أدناه هو تقنية نظيفة للغاية لتحقيق هدف أعلاه على الصعيد العالمي. يتيح الحصول على فئة مخصصة من User والتي لها بعض السمات.

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

الآن لدينا مجموعة نحتاج إلى createdDate على أساس createdDate تصاعديًا و / أو تنازليًا. لذلك دعونا نضيف وظيفة لمقارنة التاريخ.

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
    }
}

الآن يتيح امتلاك extension من Array User . في كلمات بسيطة ، يتيح إضافة بعض الأساليب فقط لتلك المصفوفات التي تحتوي فقط على كائنات User في ذلك.

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
    }
}

الاستخدام لترتيب تصاعدي

let sortedArray = someArray.sortUserByDate(.orderedAscending)

الاستخدام لترتيب تنازلي

let sortedArray = someArray.sortUserByDate(.orderedAscending)

الاستخدام لنفس الترتيب

let sortedArray = someArray.sortUserByDate(.orderedSame)

لا يمكن الوصول إلى الطريقة المذكورة أعلاه إلا إذا كان Array من النوع [User] || Array<User>


ستوفر لك مجموعات iOS 4 :)

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 قليلاً من الوصف


لقد استخدمت sortUsingFunction :: في بعض مشاريعي :

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];

تحتاج كائنات " Person الخاصة بك إلى تطبيق طريقة ، قل compare: التي تأخذ كائن Person آخر ، وإرجاع NSComparisonResult وفقًا للعلاقة بين الكائنات 2.

ثم يمكنك استدعاء sortedArrayUsingSelector: مع @selector(compare:) ويجب أن يتم ذلك.

هناك طرق أخرى ، ولكن بقدر ما أعرف لا يوجد كاكاو - equiv من واجهة قابلة Comparable . استخدام sortedArrayUsingSelector: هو على الأرجح الطريقة الأكثر sortedArrayUsingSelector: للقيام بذلك.


import operator

لفرز قائمة القواميس من خلال key = 'name':

list_of_dicts.sort(key=operator.itemgetter('name'))

لفرز قائمة القواميس بالمفتاح = "العمر":

list_of_dicts.sort(key=operator.itemgetter('age'))




ios objective-c sorting cocoa-touch nsmutablearray