NSDictionary с упорядоченными ключами - PullRequest
78 голосов
/ 18 декабря 2008

У меня есть NSDictionary (хранится в plist), который я в основном использую в качестве ассоциативного массива (строки в качестве ключей и значений). Я хочу использовать массив ключей как часть моего приложения, но я бы хотел, чтобы они были в определенном порядке (на самом деле это не тот порядок, в котором я могу написать алгоритм для их сортировки). Я всегда мог хранить отдельный массив ключей, но это кажется довольно хитрым, потому что мне всегда приходилось обновлять ключи словаря, а также значения массива и следить, чтобы они всегда соответствовали. В настоящее время я просто использую [myDictionary allKeys], но, очевидно, это возвращает их в произвольном, негарантированном порядке. Есть ли в Objective-C структура данных, которую мне не хватает? У кого-нибудь есть какие-нибудь предложения о том, как более элегантно это сделать?

Ответы [ 9 ]

23 голосов
/ 18 декабря 2008

Решение иметь связанный NSMutableArray ключей не так уж плохо. Он избегает создания подклассов NSDictionary, и если вы осторожны с написанием методов доступа, его не должно быть слишком сложно для синхронизации.

19 голосов
/ 18 октября 2010

Я опоздал на игру с реальным ответом, но вам может быть интересно изучить CHOrderedDictionary . Это подкласс NSMutableDictionary, который инкапсулирует другую структуру для поддержания порядка ключей. (Это часть CHDataStructures.framework .) Я считаю, что это удобнее, чем управлять словарем и массивом по отдельности.

Раскрытие: это код с открытым исходным кодом, который я написал. Просто надеясь, что это может быть полезно для других, сталкивающихся с этой проблемой.

15 голосов
/ 22 февраля 2013

Нет такого встроенного метода, с помощью которого вы можете получить это. Но простая логика работает для вас. Вы можете просто добавить немного числового текста перед каждым ключом, пока вы готовите словарь. Как

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 символа.

7 голосов
/ 18 декабря 2008

Если вы собираетесь создать подкласс NSDictionary, вам нужно как минимум реализовать эти методы:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying / NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (для леопарда)
    • -countByEnumeratingWithState:objects:count:

Самый простой способ сделать то, что вы хотите, это создать подкласс NSMutableDictionary, который содержит собственный NSMutableDictionary, которым он манипулирует, и NSMutableArray для хранения упорядоченного набора ключей.

Если вы никогда не собираетесь кодировать свои объекты, вы можете пропустить реализацию -encodeWithCoder: и -initWithCoder:

Все ваши реализации методов в приведенных выше 10 методах будут либо проходить напрямую через ваш размещенный словарь, либо через ваш упорядоченный массив ключей.

5 голосов
/ 20 марта 2014

Мое небольшое дополнение: сортировка по числовой клавише (с использованием сокращенных обозначений для меньшего кода)

// 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]];
}
3 голосов
/ 13 августа 2009

Быстро и грязно:

Когда вам нужно заказать словарь (в данном документе называемый «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];
            }
        }

Теперь индексный объект будет содержать соответствующие ключи в правильном порядке. Обратите внимание, что это решение не требует обязательного наличия всех ключей, что является обычной ситуацией, с которой мы имеем дело ...

2 голосов
/ 19 октября 2016

Для, Свифт 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)..<c.endIndex

        // Get the dictionary key by removing first 3 chars
        let firstKey = firstSortedKeyOfDictionary[range]

        //Print the first key
        print(firstKey)
1 голос
/ 06 сентября 2017

Минимальная реализация упорядоченного подкласса NSDictionary (на основе https://github.com/nicklockwood/OrderedDictionary). Не стесняйтесь расширять для своих нужд:

Свифт 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<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

использование

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]);
}
0 голосов
/ 25 августа 2017

Мне не очень нравится C ++, но я вижу, что все чаще и чаще я использую Objective-C ++ и std::map из стандартной библиотеки шаблонов. Это словарь, ключи которого автоматически сортируются при вставке. Он работает на удивление хорошо как с скалярными типами, так и с объектами Objective-C как ключами, так и значениями.

Если вам нужно включить массив в качестве значения, просто используйте std::vector вместо NSArray.

Одно предостережение: вы можете захотеть предоставить свою собственную функцию insert_or_assign, если только вы не можете использовать C ++ 17 (см. этот ответ ). Кроме того, вам нужно typedef ваши типы, чтобы предотвратить определенные ошибки сборки. Как только вы поймете, как использовать std::map, итераторы и т. Д., Это довольно просто и быстро.

...