J'ai un NSDictionary (stocké dans une liste) que j'utilise essentiellement comme tableau associatif (chaînes comme clés et valeurs). Je veux utiliser le tableau de clés dans le cadre de mon application, mais j'aimerais qu'elles soient dans un ordre spécifique (pas vraiment un ordre dans lequel je peux écrire un algorithme pour les trier). Je pourrais toujours stocker un tableau séparé des clés, mais cela semble un peu kludgey car je devrais toujours mettre à jour les clés du dictionnaire ainsi que les valeurs du tableau, et m'assurer qu'elles correspondent toujours. Actuellement, j'utilise simplement [myDictionary allKeys], mais cela les renvoie évidemment dans un ordre arbitraire et non garanti. Existe-t-il une structure de données dans Objective-C qui me manque? Quelqu'un a-t-il des suggestions sur la façon de le faire de manière plus élégante?
La solution d'avoir un NSMutableArray de clés associé n'est pas si mauvaise. Cela évite de sous-classer NSDictionary, et si vous faites attention à l'écriture d'accesseurs, il ne devrait pas être trop difficile de rester synchronisé.
Je suis en retard dans le jeu avec une réponse réelle, mais vous pourriez être intéressé à enquêter CHOrderedDictionary . C'est une sous-classe de NSMutableDictionary qui encapsule une autre structure pour maintenir l'ordre des clés. (Cela fait partie de CHDataStructures.framework .) Je trouve que c'est plus pratique que de gérer un dictionnaire et un tableau séparément.
Divulgation: Ceci est du code open-source que j'ai écrit. J'espère juste que cela peut être utile à d'autres personnes confrontées à ce problème.
Il n'y a pas une telle méthode intégrée à partir de laquelle vous pouvez l'acquérir. Mais une logique simple fonctionne pour vous. Vous pouvez simplement ajouter quelques textes numériques devant chaque touche pendant que vous préparez le dictionnaire. Comme
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];
Maintenant, si vous pouvez utiliser sortingArrayUsingSelector tout en obtenant toutes les clés dans le même ordre que vous placez.
NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
À l'endroit où vous souhaitez afficher les clés dans UIView, coupez simplement le caractère avant 3.
Si vous allez sous-classer NSDictionary, vous devez implémenter ces méthodes au minimum:
-count
-objectForKey:
-keyEnumerator
-removeObjectForKey:
-setObject:forKey:
-copyWithZone:
-mutableCopyWithZone:
-encodeWithCoder:
-initWithCoder:
-countByEnumeratingWithState:objects:count:
La façon la plus simple de faire ce que vous voulez est de créer une sous-classe de NSMutableDictionary qui contient son propre NSMutableDictionary qu'il manipule et un NSMutableArray pour stocker un ensemble ordonné de clés.
Si vous ne codez jamais vos objets, vous pouvez imaginer ignorer l'implémentation de -encodeWithCoder:
et -initWithCoder:
Toutes vos implémentations de méthode dans les 10 méthodes ci-dessus passeraient alors directement par votre dictionnaire hébergé ou votre tableau de clés ordonné.
Mon petit plus: le tri par clé numérique (en utilisant des notations abrégées pour un code plus petit)
// 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]];
}
Rapide et sale:
Lorsque vous devez commander votre dictionnaire (appelé ici "myDict"), procédez comme suit:
NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil];
Ensuite, lorsque vous devez commander votre dictionnaire, créez un index:
NSEnumerator *sectEnum = [ordering objectEnumerator];
NSMutableArray *index = [[NSMutableArray alloc] init];
id sKey;
while((sKey = [sectEnum nextObject])) {
if ([myDict objectForKey:sKey] != nil ) {
[index addObject:sKey];
}
}
Maintenant, l'objet * index contiendra les clés appropriées dans le bon ordre. Notez que cette solution ne nécessite pas que toutes les clés existent nécessairement, ce qui est la situation habituelle avec laquelle nous avons affaire ...
Pour, Swift. Veuillez essayer l'approche suivante
//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)
Implémentation minimale d'une sous-classe ordonnée de NSDictionary (basée sur https://github.com/nicklockwood/OrderedDictionary ). N'hésitez pas à étendre vos besoins:
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) }
@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]);
}
Je n'aime pas beaucoup C++, mais une solution que je me vois de plus en plus utiliser est d'utiliser Objective-C++ et std::map
à partir de la bibliothèque de modèles standard. Il s'agit d'un dictionnaire dont les clés sont automatiquement triées lors de l'insertion. Cela fonctionne étonnamment bien avec des types scalaires ou des objets Objective-C à la fois comme clés et comme valeurs.
Si vous devez inclure un tableau en tant que valeur, utilisez simplement std::vector
au lieu de NSArray
.
Une mise en garde est que vous voudrez peut-être fournir votre propre insert_or_assign
, sauf si vous pouvez utiliser C++ 17 (voir cette réponse ). De plus, vous devez typedef
vos types pour éviter certaines erreurs de build. Une fois que vous avez compris comment utiliser std::map
, itérateurs, etc., c'est assez simple et rapide.