ios 带有有序键的 NSDictionary
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/376090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
NSDictionary with ordered keys
提问by Andy Bourassa
I have an NSDictionary (stored in a plist) that I'm basically using as an associative array (strings as keys and values). I want to use the array of keys as part of my application, but I'd like them to be in a specific order (not really an order that I can write an algorithm to sort them into). I could always store a separate array of the keys, but that seems kind of kludgey because I'd always have to update the keys of the dictionary as well as the values of the array, and make sure they always correspond. Currently I just use [myDictionary allKeys], but obviously this returns them in an arbitrary, non-guaranteed order. Is there a data structure in Objective-C that I'm missing? Does anyone have any suggestions on how to more elegantly do this?
我有一个 NSDictionary(存储在 plist 中),我基本上用作关联数组(字符串作为键和值)。我想将键数组用作我的应用程序的一部分,但我希望它们按特定顺序排列(实际上并不是我可以编写算法将它们排序的顺序)。我总是可以存储一个单独的键数组,但这似乎有点麻烦,因为我总是必须更新字典的键以及数组的值,并确保它们始终对应。目前我只使用 [myDictionary allKeys],但显然这会以任意的、无保证的顺序返回它们。Objective-C 中是否有我遗漏的数据结构?有没有人对如何更优雅地做到这一点有任何建议?
回答by Abizern
The solution of having an associated NSMutableArray of keys isn't so bad. It avoids subclassing NSDictionary, and if you are careful with writing accessors, it shouldn't be too hard to keep synchronised.
拥有一个关联的 NSMutableArray 键的解决方案并不是那么糟糕。它避免了 NSDictionary 的子类化,如果你在编写访问器时很小心,保持同步应该不会太难。
回答by Quinn Taylor
I'm late to the game with an actual answer, but you might be interested to investigate CHOrderedDictionary. It's a subclass of NSMutableDictionary which encapsulates another structure for maintaining key ordering. (It's part of CHDataStructures.framework.) I find it to be more convenient than managing a dictionary and array separately.
我迟到了实际答案,但您可能有兴趣调查CHOrderedDictionary。它是 NSMutableDictionary 的子类,它封装了另一个用于维护键排序的结构。(它是CHDataStructures.framework的一部分。)我发现它比分别管理字典和数组更方便。
Disclosure: This is open-source code which I wrote. Just hoping it may be useful to others facing this problem.
披露:这是我编写的开源代码。只是希望它对面临此问题的其他人有用。
回答by Rajan Twanabashu
There is no such inbuilt method from which you can acquire this. But a simple logic work for you. You can simply add few numeric text in front of each key while you prepare the dictionary. Like
没有这样的内置方法可以从中获得它。但是一个简单的逻辑对你有用。在准备字典时,您可以简单地在每个键前添加一些数字文本。喜欢
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];
Now if you can use sortingArrayUsingSelector while getting all keys in the same order as you place.
现在,如果您可以使用 sortingArrayUsingSelector 同时以与您放置的顺序相同的顺序获取所有键。
NSArray *arr = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
At the place where you want to display keys in UIView, just chop off the front 3 character.
在UIView中要显示key的地方,砍掉前面3个字符即可。
回答by Ashley Clark
If you're going to subclass NSDictionary you need to implement these methods as a minimum:
如果您打算继承 NSDictionary,您至少需要实现这些方法:
- NSDictionary
-count
-objectForKey:
-keyEnumerator
- NSMutableDictionary
-removeObjectForKey:
-setObject:forKey:
- NSCopying/NSMutableCopying
-copyWithZone:
-mutableCopyWithZone:
- NSCoding
-encodeWithCoder:
-initWithCoder:
- NSFastEnumeration (for Leopard)
-countByEnumeratingWithState:objects:count:
- NSD字典
-count
-objectForKey:
-keyEnumerator
- NSMutableDictionary
-removeObjectForKey:
-setObject:forKey:
- NSCopying/NSMutableCopying
-copyWithZone:
-mutableCopyWithZone:
- 编码
-encodeWithCoder:
-initWithCoder:
- NSFastEnumeration (用于 Leopard)
-countByEnumeratingWithState:objects:count:
The easiest way to do what you want is to make a subclass of NSMutableDictionary that contains its' own NSMutableDictionary that it manipulates and an NSMutableArray to store an ordered set of keys.
做你想做的最简单的方法是创建一个 NSMutableDictionary 的子类,它包含它自己操作的 NSMutableDictionary 和一个 NSMutableArray 来存储一组有序的键。
If you're never going to encode your objects you could conceivable skip implementing -encodeWithCoder:
and -initWithCoder:
如果你永远不会编码你的对象,你可以想象跳过实现-encodeWithCoder:
和-initWithCoder:
All of your method implementations in the 10 methods above would then either go directly through your hosted dictionary or your ordered key array.
上面 10 种方法中的所有方法实现都将直接通过您的托管字典或您的有序键数组。
回答by BananaAcid
My little addition: sorting by numeric key (Using shorthand notations for smaller code)
我的一点补充:按数字键排序(对较小的代码使用速记符号)
// 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]];
}
回答by Adam Prall
Quick 'n dirty:
快速肮脏:
When you need to order your dictionary (herein called “myDict”), do this:
当您需要订购您的词典(此处称为“myDict”)时,请执行以下操作:
NSArray *ordering = [NSArray arrayWithObjects: @"Thing",@"OtherThing",@"Last Thing",nil];
Then, when you need to order your dictionary, create an index:
然后,当您需要订购字典时,请创建一个索引:
NSEnumerator *sectEnum = [ordering objectEnumerator];
NSMutableArray *index = [[NSMutableArray alloc] init];
id sKey;
while((sKey = [sectEnum nextObject])) {
if ([myDict objectForKey:sKey] != nil ) {
[index addObject:sKey];
}
}
Now, the *index object will contain the appropriate keys in the correct order. Note that this solution does not require that all the keys necessarily exist, which is the usual situation we're dealing with...
现在, *index 对象将以正确的顺序包含适当的键。请注意,此解决方案不需要所有密钥都必须存在,这是我们正在处理的常见情况......
回答by Dinesh
For, Swift 3. Please try out the following approach
对于,斯威夫特 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 { 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)
}
}
}
.localizedCaseInsensitiveCompare() == 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)
回答by C?ur
Minimal implementation of an ordered subclass of NSDictionary (based on https://github.com/nicklockwood/OrderedDictionary). Feel free to extend for your needs:
NSDictionary 有序子类的最小实现(基于https://github.com/nicklockwood/OrderedDictionary)。随意扩展您的需求:
Swift 3 and 4
斯威夫特 3 和 4
let normalDic = ["hello": "world", "foo": "bar"]
// initializing empty ordered dictionary
let orderedDic = MutableOrderedDictionary()
// copying normalDic in orderedDic after a sort
normalDic.sorted { @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
.0.compare(.0) == .orderedAscending }
.forEach { orderedDic.setObject(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]);
}
.value, forKey: ##代码##.key) }
// from now, looping on orderedDic will be done in the alphabetical order of the keys
orderedDic.forEach { print(##代码##) }
usage
用法
##代码##Objective-C
目标-C
##代码##usage
用法
##代码##回答by Martin Winter
I don't like C++ very much, but one solution that I see myself using more and more is to use Objective-C++ and std::map
from the Standard Template Library. It is a dictionary whose keys are automatically sorted on insertion. It works surprisingly well with either scalar types or Objective-C objects both as keys and as values.
我不太喜欢 C++,但我发现自己越来越多地使用的一种解决方案是使用 Objective-C++ 和std::map
标准模板库。它是一个字典,其键在插入时自动排序。无论是作为键还是作为值的标量类型或 Objective-C 对象,它都非常出色。
If you need to include an array as a value, just use std::vector
instead of NSArray
.
如果您需要包含一个数组作为值,只需使用std::vector
代替NSArray
。
One caveat is that you might want to provide your own insert_or_assign
function, unless you can use C++17 (see this answer). Also, you need to typedef
your types to prevent certain build errors. Once you figure out how to use std::map
, iterators etc., it is pretty straightforward and fast.
一个警告是您可能想要提供自己的insert_or_assign
函数,除非您可以使用 C++17(请参阅此答案)。此外,您需要使用typedef
您的类型来防止某些构建错误。一旦你弄清楚如何使用std::map
、迭代器等,它就会变得非常简单和快速。