NSDictionary с упорядоченными ключами
Мне любопытно, если это ситуация, в которой оказались другие люди. У меня есть NSDictionary (хранится в plist), который я использую в основном как ассоциативный массив (строки как ключи и значения). Я хочу использовать массив ключей как часть моего приложения, но я бы хотел, чтобы они были в определенном порядке (на самом деле не такой порядок, чтобы я мог написать алгоритм для их сортировки). Я всегда мог хранить отдельный массив ключей, но это похоже на kludgey, потому что мне всегда нужно обновлять ключи словаря, а также значения массива и следить за тем, чтобы они всегда соответствовали друг другу. В настоящее время я просто использую [myDictionary allKeys], но, очевидно, это возвращает их в произвольном, не гарантированном порядке. Есть ли структура данных в Objective-C, которую мне не хватает? Есть ли у кого-нибудь какие-либо предложения о том, как более элегантно это сделать?
- Редактирование Info.plist возможно программно?
- Процедура отправки сообщений iOS 6 Facebook заканчивается ошибкой «remote_app_id не соответствует сохраненному id»
- Создание UIImage в форме круга
- UIImage для base64 Кодирование строк
- Метод sizeWithFont устарел. boundingRectWithSize возвращает неожиданное значение
- Рисование формы волны с помощью AVAssetReader
- NSMutableArray addObject не работает
- Вам нужно создать NSAutoreleasePool в блоке в GCD?
Решение иметь связанный с ним NSMutableArray ключей не так уж плохо. Он избегает подclassификации NSDictionary, и если вы внимательно относитесь к написанию аксессуаров, не должно быть слишком сложно синхронизироваться.
Я опаздываю на игру с реальным ответом, но вам может быть интересно исследовать CHOrderedDictionary . Это подclass NSMutableDictionary, который инкапсулирует другую структуру для поддержания порядка клавиш. (Это часть CHDataStructures.framework .) Я считаю, что это более удобно, чем управление словарем и массивом по отдельности.
Раскрытие: Это открытый код, который я написал. Просто надеюсь, что это может быть полезно для других, сталкивающихся с этой проблемой.
Нет такого встроенного метода, из которого вы можете его приобрести. Но простая логическая работа для вас. Вы можете просто добавить несколько числовых символов перед каждой клавишей во время подготовки словаря. подобно
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];
Теперь, если вы можете использовать sortingArrayUsingSelector, получая все ключи в том же порядке, что и вы.
NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
В том месте, где вы хотите отображать ключи в UIView, просто отрубите передний символ 3.
Если вы собираетесь подclassифицировать NSDictionary, вам необходимо реализовать эти методы как минимум:
- NSDictionary
-
-count
-
-objectForKey:
-
-keyEnumerator
-
- NSMutableDictionary
-
-removeObjectForKey:
-
-setObject:forKey:
-
- NSCopying / NSMutableCopying
-
-copyWithZone:
-
-mutableCopyWithZone:
-
- NSCoding
-
-encodeWithCoder:
-
-initWithCoder:
-
- NSFastEnumeration (для Leopard)
-
-countByEnumeratingWithState:objects:count:
-
Самый простой способ сделать то, что вы хотите, – сделать подclass NSMutableDictionary, который содержит свой собственный NSMutableDictionary, который он управляет, и NSMutableArray для хранения упорядоченного набора ключей.
Если вы никогда не собираетесь кодировать свои объекты, вы могли бы пропустить реализацию -encodeWithCoder:
и -initWithCoder:
Все реализации вашего метода в 10 описанных выше методах затем будут либо проходить через ваш размещенный словарь, либо ваш упорядоченный массив ключей.
Мое небольшое дополнение: сортировка по числовому ключу (использование сокращенных обозначений для меньшего кода)
// 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]]; }
Быстрое загрязнение:
Когда вам нужно заказать свой словарь (здесь называется myDict), сделайте следующее:
NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil];
Затем, когда вам нужно заказать словарь, создайте индекс:
NSEnumerator *sectEnum = [ordering objectEnumerator]; NSMutableArray *index = [[NSMutableArray alloc] init]; id sKey; while((sKey = [sectEnum nextObject])) { if ([myDict objectForKey:sKey] != nil ) { [index addObject:sKey]; } }
Теперь объект * index будет содержать соответствующие ключи в правильном порядке. Обратите внимание, что это решение не требует, чтобы все ключи существовали, что является обычной ситуацией, с которой мы имеем дело …
For, Swift 3 . Попробуйте воспользоваться следующим подходом
//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)..
Минимальная реализация упорядоченного подclassа NSDictionary (на основе https://github.com/nicklockwood/OrderedDictionary ). Не стесняйтесь распространяться на ваши потребности:
Swift 3 и 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) } } }
Применение
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) }
Objective-C
@interface MutableOrderedDictionary<__covariant KeyType, __covariant ObjectType> : NSDictionary @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
Применение
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]); }
Мне не очень нравится C ++, но одно решение, которое я вижу, что я использую все больше и больше, – использовать Objective-C ++ и std::map
из стандартной библиотеки шаблонов. Это словарь, ключи которого автоматически сортируются при вставке. Он работает на удивление хорошо с использованием либо скалярных типов, либо объектов Objective-C как ключей, так и значений.
Если вам нужно включить массив в качестве значения, просто используйте std::vector
вместо NSArray
.
Одно из предостережений заключается в том, что вы можете предоставить свою собственную функцию insert_or_assign
, если вы не можете использовать C ++ 17 (см. Этот ответ ). Кроме того, вам необходимо typedef
для предотвращения определенных ошибок сборки. Как только вы выясните, как использовать std::map
, iteratorы и т. Д., Это довольно просто и быстро.