带有有序键的 NSDictionary

我有一个 NSDictionary (存储在 plist 中) ,基本上用作关联数组(字符串作为键和值)。我希望将键数组作为应用程序的一部分,但是我希望它们按特定的顺序排列(并不是我可以编写算法将它们排序的顺序)。我总是可以存储一个单独的键数组,但这似乎有点笨拙,因为我总是必须更新字典中的键以及数组的值,并确保它们始终对应。目前我只是使用[ myDictionary allKeys ] ,但显然这将以任意、无保证的顺序返回它们。在 Objective-C 中是否有我遗漏的数据结构?有人对如何更优雅地完成这项工作有什么建议吗?

75386 次浏览

拥有一个关联的 NSMutableArray 键的解决方案并没有那么糟糕。它避免了对 NSDictionary 进行子类化,如果您注意编写访问器,保持同步应该不会太难。

如果你想要子类 NSDictionary,你需要实现这些方法作为最低限度:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying/NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFast枚举 < em > (对于豹)
    • -countByEnumeratingWithState:objects:count:

最简单的方法是创建一个 NSMutableDictionary 的子类,它包含它自己操作的 NSMutableDictionary,以及一个 NSMutableArray 来存储一组有序的键。

如果你永远不打算编码你的对象,你可以想象跳过实现 -encodeWithCoder:-initWithCoder:

上面10个方法中的所有方法实现都将直接通过宿主字典或有序键数组。

速战速决:

当你需要订购你的字典(在这里称为“ myDect”)时,这样做:

     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 对象将以正确的顺序包含相应的键。注意,这个解决方案并不要求所有键都必须存在,这是我们正在处理的通常情况..。

我已经迟到了,不过你可能有兴趣研究一下 CHOrderedDictionary。它是 NSMutableDictionary 的一个子类,封装了另一个用于维护键顺序的结构。(这是 框架的一部分。)我发现它比分别管理字典和数组更方便。

说明: 这是我写的开源代码,希望对其他面临这个问题的人有用。

没有这样的内置方法,您可以从中获得这一点。但是一个简单的逻辑为你工作。在准备字典时,只需在每个键前面添加几个数字文本即可。喜欢

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

现在,如果你可以使用排序数组使用选择器,同时得到所有的键在同样的顺序,因为你的地方。

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

在您想要在 UIView 中显示键的位置,只需删除前面的3个字符。

我的小补充: 按数字键排序(对较小的代码使用速记符号)

// 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。 请尝试以下方法

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

我不太喜欢 C + + ,但是我认为自己使用越来越多的一个解决方案是使用 Objective-C + + 和标准模板库的 std::map。这是一个字典的关键字是自动排序的插入。无论是将标量类型还是 Objective-C 对象作为键还是作为值,它都能够很好地工作。

如果需要包含一个数组作为值,只需使用 std::vector而不是 NSArray

需要注意的一点是,您可能希望提供自己的 insert_or_assign函数,除非您可以使用 C + + 17(参见 这个答案)。此外,您需要 typedef您的类型,以防止某些生成错误。一旦你弄清楚如何使用 std::map,迭代器等,它是相当简单和快速。

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

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