Mam NSDictionary (przechowywany w plistie), którego zasadniczo używam jako tablicy asocjacyjnej (ciągi znaków jako klucze i wartości). Chcę użyć tablicy kluczy jako części mojej aplikacji, ale chciałbym, aby były w określonej kolejności (nie w takiej kolejności, w jakiej mogę napisać algorytm, aby je posortować). Zawsze mógłbym przechowywać oddzielną tablicę kluczy, ale wydaje się to trochę niezdarne, ponieważ zawsze musiałem aktualizować klucze słownika, a także wartości tablicy i upewnić się, że zawsze odpowiadają. Obecnie po prostu używam [myDictionary allKeys], ale oczywiście zwraca to je w dowolnej, niegwarantowanej kolejności. Czy brakuje mi struktury danych w Objective-C? Czy ktoś ma jakieś sugestie, jak to zrobić bardziej elegancko?
źródło
Spóźniłem się na grę z rzeczywistą odpowiedzią, ale możesz być zainteresowany zbadaniem CHOrderedDictionary . Jest to podklasa NSMutableDictionary, która zawiera inną strukturę do utrzymywania kolejności kluczy. (Jest częścią CHDataStructures.framework .) Uważam, że jest to wygodniejsze niż osobne zarządzanie słownikiem i tablicą.
Ujawnienie: to jest kod open source, który napisałem. Mam tylko nadzieję, że może to być przydatne dla innych osób borykających się z tym problemem.
źródło
Nie ma takiej wbudowanej metody, z której można to uzyskać. Ale wystarczy prosta logika. Podczas przygotowywania słownika możesz po prostu dodać kilka znaków numerycznych przed każdym klawiszem. Lubić
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys: @"01.Created",@"cre", @"02.Being Assigned",@"bea", @"03.Rejected",@"rej", @"04.Assigned",@"ass", @"05.Scheduled",@"sch", @"06.En Route",@"inr", @"07.On Job Site",@"ojs", @"08.In Progress",@"inp", @"09.On Hold",@"onh", @"10.Completed",@"com", @"11.Closed",@"clo", @"12.Cancelled", @"can", nil];
Teraz, jeśli możesz użyć sortingArrayUsingSelector podczas pobierania wszystkich kluczy w tej samej kolejności, w jakiej umieścisz.
NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
W miejscu, w którym chcesz wyświetlić klawisze w UIView, po prostu odetnij 3 przednie znaki.
źródło
Jeśli zamierzasz podklasę NSDictionary, musisz zaimplementować co najmniej następujące metody:
-count
-objectForKey:
-keyEnumerator
-removeObjectForKey:
-setObject:forKey:
-copyWithZone:
-mutableCopyWithZone:
-encodeWithCoder:
-initWithCoder:
-countByEnumeratingWithState:objects:count:
Najłatwiejszym sposobem zrobienia tego, co chcesz, jest utworzenie podklasy NSMutableDictionary, która zawiera własny NSMutableDictionary, który manipuluje, oraz NSMutableArray do przechowywania uporządkowanego zestawu kluczy.
Jeśli nigdy nie zamierzasz kodować swoich obiektów, możesz sobie wyobrazić pominięcie implementacji
-encodeWithCoder:
i-initWithCoder:
Wszystkie implementacje metod w powyższych 10 metodach przeszłyby bezpośrednio przez Twój hostowany słownik lub uporządkowaną tablicę kluczy.
źródło
Mój mały dodatek: sortowanie według klucza numerycznego (używanie skrótów do mniejszego kodu)
// the resorted result array NSMutableArray *result = [NSMutableArray new]; // the source dictionary - keys may be Ux timestamps (as integer, wrapped in NSNumber) NSDictionary *dict = @{ @0: @"a", @3: @"d", @1: @"b", @2: @"c" }; {// do the sorting to result NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)]; for (NSNumber *n in arr) [result addObject:dict[n]]; }
źródło
Szybko i brudno:
Jeśli chcesz zamówić słownik (zwany dalej „myDict”), zrób to:
NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil];
Następnie, gdy chcesz zamówić słownik, utwórz indeks:
NSEnumerator *sectEnum = [ordering objectEnumerator]; NSMutableArray *index = [[NSMutableArray alloc] init]; id sKey; while((sKey = [sectEnum nextObject])) { if ([myDict objectForKey:sKey] != nil ) { [index addObject:sKey]; } }
Teraz obiekt indeksu * będzie zawierał odpowiednie klucze we właściwej kolejności. Zauważ, że to rozwiązanie nie wymaga, aby wszystkie klucze istniały, co jest typową sytuacją, z którą mamy do czynienia ...
źródło
Dla Swift 3 . Wypróbuj następujące podejście
//Sample Dictionary let dict: [String: String] = ["01.One": "One", "02.Two": "Two", "03.Three": "Three", "04.Four": "Four", "05.Five": "Five", "06.Six": "Six", "07.Seven": "Seven", "08.Eight": "Eight", "09.Nine": "Nine", "10.Ten": "Ten" ] //Print the all keys of dictionary print(dict.keys) //Sort the dictionary keys array in ascending order let sortedKeys = dict.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending } //Print the ordered dictionary keys print(sortedKeys) //Get the first ordered key var firstSortedKeyOfDictionary = sortedKeys[0] // Get range of all characters past the first 3. let c = firstSortedKeyOfDictionary.characters let range = c.index(c.startIndex, offsetBy: 3)..<c.endIndex // Get the dictionary key by removing first 3 chars let firstKey = firstSortedKeyOfDictionary[range] //Print the first key print(firstKey)
źródło
Minimalna implementacja uporządkowanej podklasy NSDictionary (na podstawie https://github.com/nicklockwood/OrderedDictionary ). Zapraszam do przedłużenia dla swoich potrzeb:
Swift 3 i 4
class MutableOrderedDictionary: NSDictionary { let _values: NSMutableArray = [] let _keys: NSMutableOrderedSet = [] override var count: Int { return _keys.count } override func keyEnumerator() -> NSEnumerator { return _keys.objectEnumerator() } override func object(forKey aKey: Any) -> Any? { let index = _keys.index(of: aKey) if index != NSNotFound { return _values[index] } return nil } func setObject(_ anObject: Any, forKey aKey: String) { let index = _keys.index(of: aKey) if index != NSNotFound { _values[index] = anObject } else { _keys.add(aKey) _values.add(anObject) } } }
stosowanie
let normalDic = ["hello": "world", "foo": "bar"] // initializing empty ordered dictionary let orderedDic = MutableOrderedDictionary() // copying normalDic in orderedDic after a sort normalDic.sorted { $0.0.compare($1.0) == .orderedAscending } .forEach { orderedDic.setObject($0.value, forKey: $0.key) } // from now, looping on orderedDic will be done in the alphabetical order of the keys orderedDic.forEach { print($0) }
Cel C
@interface MutableOrderedDictionary<__covariant KeyType, __covariant ObjectType> : NSDictionary<KeyType, ObjectType> @end @implementation MutableOrderedDictionary { @protected NSMutableArray *_values; NSMutableOrderedSet *_keys; } - (instancetype)init { if ((self = [super init])) { _values = NSMutableArray.new; _keys = NSMutableOrderedSet.new; } return self; } - (NSUInteger)count { return _keys.count; } - (NSEnumerator *)keyEnumerator { return _keys.objectEnumerator; } - (id)objectForKey:(id)key { NSUInteger index = [_keys indexOfObject:key]; if (index != NSNotFound) { return _values[index]; } return nil; } - (void)setObject:(id)object forKey:(id)key { NSUInteger index = [_keys indexOfObject:key]; if (index != NSNotFound) { _values[index] = object; } else { [_keys addObject:key]; [_values addObject:object]; } } @end
stosowanie
NSDictionary *normalDic = @{@"hello": @"world", @"foo": @"bar"}; // initializing empty ordered dictionary MutableOrderedDictionary *orderedDic = MutableOrderedDictionary.new; // copying normalDic in orderedDic after a sort for (id key in [normalDic.allKeys sortedArrayUsingSelector:@selector(compare:)]) { [orderedDic setObject:normalDic[key] forKey:key]; } // from now, looping on orderedDic will be done in the alphabetical order of the keys for (id key in orderedDic) { NSLog(@"%@:%@", key, orderedDic[key]); }
źródło
Nie bardzo lubię C ++, ale jednym z rozwiązań, których używam coraz częściej, jest użycie Objective-C ++ i
std::map
ze standardowej biblioteki szablonów. Jest to słownik, którego klucze są automatycznie sortowane po wstawieniu. Działa zaskakująco dobrze zarówno w przypadku typów skalarnych, jak i obiektów Objective-C, zarówno jako kluczy, jak i wartości.Jeśli chcesz dołączyć tablicę jako wartość, po prostu użyj
std::vector
zamiastNSArray
.Jedynym zastrzeżeniem jest to, że możesz chcieć udostępnić własną
insert_or_assign
funkcję, chyba że możesz użyć C ++ 17 (zobacz tę odpowiedź ). Ponadto musisz miećtypedef
swoje typy, aby zapobiec niektórym błędom kompilacji. Kiedy już dowiesz się, jak używaćstd::map
, iteratorów itp., Jest to dość proste i szybkie.źródło