Frage NSDictionary mit geordneten Schlüsseln


Ich bin neugierig, ob dies eine Situation ist, in der sich andere Leute befinden. Ich habe ein NSDictionary (in einem Plist gespeichert), das ich grundsätzlich als assoziatives Array benutze (Strings als Schlüssel und Werte). Ich möchte das Array von Schlüsseln als Teil meiner Anwendung verwenden, aber ich möchte, dass sie sich in einer bestimmten Reihenfolge befinden (nicht wirklich eine Reihenfolge, in der ich einen Algorithmus schreiben kann, um sie zu sortieren). Ich könnte immer ein separates Array der Schlüssel speichern, aber das scheint irgendwie kludsey zu sein, weil ich immer die Schlüssel des Wörterbuchs sowie die Werte des Arrays aktualisieren und sicherstellen muss, dass sie immer übereinstimmen. Momentan benutze ich einfach [myDictionary allKeys], aber offensichtlich gibt dies sie in einer willkürlichen, nicht garantierten Reihenfolge zurück. Gibt es in Objective-C eine Datenstruktur, die mir fehlt? Hat jemand irgendwelche Vorschläge, wie man das eleganter macht?


76
2017-12-17 21:27


Ursprung


Antworten:


Die Lösung, ein NSMutableArray von Schlüsseln zu haben, ist nicht so schlecht. Es vermeidet das Unterklassifizieren von NSDictionary, und wenn Sie mit dem Schreiben von Accessoren vorsichtig sind, sollte es nicht zu schwer sein, synchron zu bleiben.


23
2017-12-17 22:19



Ich bin mit einer tatsächlichen Antwort zu spät zum Spiel, aber Sie könnten interessiert sein zu untersuchen CHOrderedDictionary. Es ist eine Unterklasse von NSMutableDictionary, die eine weitere Struktur zum Aufrechterhalten der Schlüsselreihenfolge kapselt. (Es ist ein Teil von CHDataStructures.Framework.) Ich finde es bequemer, als ein Wörterbuch und ein Array getrennt zu verwalten.

Disclosure: Das ist Open-Source-Code, den ich geschrieben habe. Ich hoffe nur, dass es für andere, die mit diesem Problem konfrontiert sind, nützlich sein könnte.


18
2017-10-18 19:04



Es gibt keine solche eingebaute Methode, von der Sie dies erwerben können. Aber eine einfache Logik funktioniert für dich. Sie können vor jeder Taste einfach einen numerischen Text einfügen, während Sie das Wörterbuch vorbereiten. Mögen

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

Jetzt, wenn Sie sortingArrayUsingSelector verwenden können, während Sie alle Schlüssel in der gleichen Reihenfolge erhalten wie Sie.

NSArray *arr =  [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];

An der Stelle, an der Sie Schlüssel in UIView anzeigen möchten, hacken Sie einfach die vorderen 3 Zeichen ab.


15
2018-02-22 09:14



Wenn Sie NSDictionary ableiten, müssen Sie diese Methoden mindestens implementieren:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying / NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCodierung
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (für Leopard)
    • -countByEnumeratingWithState:objects:count:

Der einfachste Weg, um das zu tun, was Sie wollen, ist, eine Unterklasse von NSMutableDictionary zu erstellen, die ihr eigenes NSMutableDictionary enthält, das sie bearbeitet, und ein NSMutableArray, um eine geordnete Menge von Schlüsseln zu speichern.

Wenn Sie Ihre Objekte niemals kodieren, könnten Sie die Implementierung überspringen -encodeWithCoder: und -initWithCoder:

Alle Ihre Methodenimplementierungen in den obigen 10 Methoden würden dann entweder direkt durch Ihr gehostetes Wörterbuch oder Ihr geordnetes Schlüssel-Array gehen.


7
2017-12-18 04:40



Mein kleiner Zusatz: Sortierung nach numerischem Schlüssel (Kurzschrift für kleineren Code verwenden)

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

5
2018-03-20 13:58



Schnell 'n dreckig:

Wenn Sie Ihr Wörterbuch bestellen müssen (hier "myDict" genannt), tun Sie Folgendes:

     NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil];

Erstellen Sie dann einen Index, wenn Sie Ihr Wörterbuch bestellen müssen:

    NSEnumerator *sectEnum = [ordering objectEnumerator];
    NSMutableArray *index = [[NSMutableArray alloc] init];
        id sKey;
        while((sKey = [sectEnum nextObject])) {
            if ([myDict objectForKey:sKey] != nil ) {
                [index addObject:sKey];
            }
        }

Jetzt enthält das * index-Objekt die entsprechenden Schlüssel in der richtigen Reihenfolge. Beachten Sie, dass diese Lösung nicht erfordert, dass alle Schlüssel notwendigerweise existieren, was die übliche Situation ist, mit der wir es zu tun haben ...


3
2017-08-13 03:47



Für, Swift 3. Bitte versuchen Sie es wie folgt

        //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)

2
2017-10-19 12:25



Minimale Implementierung einer geordneten Unterklasse von NSDictionary (basierend auf https://github.com/nicklockwood/OrderedDictionary). Fühlen Sie sich frei, für Ihre Bedürfnisse zu verlängern:

Schnell 3 und 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)
        }
    }
}

Verwendung

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

Ziel 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

Verwendung

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

1
2017-09-06 14:15



Ich mag C ++ nicht sehr, aber eine Lösung, die ich selbst immer häufiger benutze, ist die Verwendung von Objective-C ++ und std::map aus der Standardvorlagenbibliothek. Es ist ein Wörterbuch, dessen Schlüssel beim Einfügen automatisch sortiert werden. Es funktioniert überraschend gut mit skalaren Typen oder Objective-C-Objekten, sowohl als Schlüssel als auch als Werte.

Wenn Sie ein Array als Wert angeben müssen, verwenden Sie einfach std::vector Anstatt von NSArray.

Ein Vorbehalt ist, dass Sie vielleicht Ihre eigenen zur Verfügung stellen möchten insert_or_assign Funktion, es sei denn, Sie können C ++ 17 verwenden (siehe diese Antwort). Auch müssen Sie typedef Ihre Typen, um bestimmte Buildfehler zu vermeiden. Sobald Sie herausfinden, wie man verwendet std::map, Iteratoren etc., ist es ziemlich einfach und schnell.


0
2017-08-25 13:45