カスタムオブジェクトを含むNSMutableArrayをソートするにはどうすればよいですか?
-
03-07-2019 - |
質問
やりたいことはとても簡単に思えますが、ウェブ上で答えが見つかりません。オブジェクトのNSMutableArray
があり、それらが「個人」オブジェクトであるとしましょう。 NSDate
をPerson.birthDateでソートします。これは<=>です。
このメソッドと関係があると思います:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
Javaでは、オブジェクトにComparableを実装させるか、Collections.sortをインラインカスタムコンパレータと共に使用します... Objective-Cでどのようにこれを行いますか?
解決
比較方法
オブジェクトに比較メソッドを実装します:
- (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に依存しているためです。 NSTableView
メソッドの主な利点は、コードではなくデータを使用して並べ替え順序を定義する方法を提供することです。ユーザーがヘッダー行をクリックして<=>をソートできるように設定します。
他のヒント
NSMutableArray
メソッドを参照 sortUsingFunction:context:
2つのオブジェクト(2つのPerson
オブジェクトを比較するため、NSComparisonResult
型)と context を受け取る compare 関数を設定する必要があります。パラメータ。
2つのオブジェクトはNSOrderedAscending
の単なるインスタンスです。 3番目のオブジェクトは文字列です。 @ <!> quot; birthDate <!> quot;。
この関数はPersonA.birthDate
を返します。PersonB.birthDate
<!> ltの場合はNSOrderedDescending
を返します。 NSOrderedSame
。 <=> <!> gt;の場合、<=>を返します。 <=>。最後に、<=> == <=>の場合、<=>を返します。
これは大まかな擬似コードです。 1つの日付が<!> quot; less <!> quot;、<!> quot; more <!> quot;であることの意味を具体化する必要があります。または<!> quot; equal <!> quot;別の日付に(エポックからの秒数の比較など):
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;
}
インライン化は、これを多く行う場合、おそらくこれを少しスピードアップする可能性があります。
ブロックを使用してiOS 4でこれを行いました。 idからクラス型に配列の要素をキャストする必要がありました。 この場合、pointsというプロパティを持つScoreというクラスでした。
また、配列の要素が適切な型でない場合の対処方法を決定する必要があります。この例では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;
PS:これは降順でソートしています。
iOS 4以降では、並べ替えにブロックを使用することもできます。
この特定の例では、配列内のオブジェクトに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];
注:<!> quot; sorted <!> quot;配列は自動解放されます。
すべてを試しましたが、これでうまくいきました。クラスに<!> quot; crimeScene
<!> quot;という名前の別のクラスがあり、<!> quot; <=> <!> quot;のプロパティでソートしたい。
これは魅力のように機能します:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
Georg Sch <!>#246; llyの2番目の答えですが、それでも問題ありません。
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];
ありがとう、うまく機能しています...
あなたのPerson
オブジェクトは、別のcompare:
オブジェクトを受け取るNSComparisonResult
などのメソッドを実装し、2つのオブジェクト間の関係に従ってsortedArrayUsingSelector:
を返す必要があります。
その後、@selector(compare:)
でComparable
を呼び出します。実行する必要があります。
他の方法もありますが、私が知る限り、<=>インターフェースのCocoa-equivはありません。 <=>を使用するのがおそらく最も簡単な方法です。
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 ちょっとした説明
NSMutableArray
には、sortUsingSelector
メソッドを使用します。新しいインスタンスを作成せずに、場所をソートします。
目的に応じて、次の一般的な方法を使用できます。問題が解決するはずです。
//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];
NSNumbers
の配列を並べ替えるだけの場合は、1回の呼び出しで並べ替えることができます。
[arrayToSort sortUsingSelector: @selector(compare:)];
これは、配列内のオブジェクト(NSNumber
オブジェクト)がcompareメソッドを実装するために機能します。 NSString
オブジェクト、または比較メソッドを実装するカスタムデータオブジェクトの配列に対しても同じことができます。
コンパレータブロックを使用したコードの例を次に示します。各辞書がキー<!> 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;
}];
上記のコードは、各ソートキーの整数値を取得し、それらを比較する作業を、その方法の実例として行っています。 <=>オブジェクトは比較メソッドを実装しているため、より簡単に書き換えることができます。
#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];
}];
またはコンパレータの本体を1行にまで蒸留することもできます。
return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
コードは読みやすく、デバッグしやすいため、単純なステートメントと多くの一時変数を好む傾向があります。コンパイラーはとにかく一時変数を最適化するため、オールインワンバージョンの利点はありません。
Linq to ObjectiveC というカテゴリメソッドの小さなライブラリを作成しました。より簡単なこと。キーセレクターで sort メソッドを使用すると、次のようにbirthDate
でソートできます。
NSArray* sortedByBirthDate = [input sort:^id(id person) {
return [person birthDate];
}]
カスタム要件に基づいてマルチレベルのソートを実行しました。
//値をソート
[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
一部のプロジェクトで 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];
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted
{
NSArray *sortedArray;
sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
return [a compare:b];
}];
return [sortedArray mutableCopy];
}
NSSortDescriptorを使用して、カスタムオブジェクトでNSMutableArrayをソートします
NSSortDescriptor *sortingDescriptor;
sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
ソート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];
NSComparatorを使用して並べ替え
カスタムオブジェクトを並べ替える場合は、NSComparator
を指定する必要があります。これは、カスタムオブジェクトの比較に使用されます。ブロックは、2つのオブジェクトの順序を示すNSComparisonResult
値を返します。したがって、配列全体をソートするには、次のように<=>を使用します。
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
return [e1.firstname compare:e2.firstname];
}];
NSSortDescriptorを使用したソート
例として、カスタムクラスのインスタンスを含む配列があり、Employeeにはfirstname、lastname、およびageの属性があると仮定します。次の例は、年齢のキーで昇順に配列の内容をソートするために使用できるNSSortDescriptorを作成する方法を示しています。
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
カスタム比較を使用してソート
名前は文字列です。ユーザーに表示する文字列を並べ替えるときは、常にローカライズされた比較を使用する必要があります。多くの場合、大文字と小文字を区別しない比較も実行します。次に、(localizedStandardCompare :)を使用して、姓と名で配列を並べる例を示します。
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];
参照および詳細な議論については、以下を参照してください。
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/
Swiftのプロトコルと関数型プログラミングにより、クラスをComparableプロトコルに適合させ、プロトコルに必要なメソッドを実装し、sorted(by:)高次関数を使用してソートされた配列を作成するだけで非常に簡単になります。可変配列を使用する必要があります。
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)
私の場合、<!> quot; sortedArrayUsingComparator <!> quot;を使用します。配列をソートします。以下のコードを見てください。
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
Swiftで配列をソート
For Swifty
以下の人は、グローバルに上記の目標を達成するための非常にクリーンなテクニックです。いくつかの属性を持つUser
のカスタムクラスの例を見てみましょう。
class User: NSObject {
var id: String?
var name: String?
var email: String?
var createdDate: Date?
}
これで、昇順および/または降順で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]
にすることができます。簡単に言えば、Array<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(.orderedSame)
同じ注文の使用
<*><=>の上記のメソッドは、<=>がタイプの場合のみアクセス可能です <=> || <=>
sortDescriptorを作成する必要があります。次に、次のようにsortDescriptorを使用してnsmutablearrayをソートできます。
let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
print(array)
ネストされたオブジェクトにこのように使用します
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];
lastRouteは1つのオブジェクトであり、そのオブジェクトはtoオブジェクト、そのtoオブジェクトはlastname文字列値を保持します。
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(@"===============================");