具有有序键的 NSDictionary

发布于 2024-07-09 18:21:26 字数 282 浏览 10 评论 0原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

女中豪杰 2024-07-16 18:21:26

拥有关联的 NSMutableArray 键的解决方案并不是那么糟糕。 它避免了 NSDictionary 的子类化,并且如果您小心地编写访问器,那么保持同步应该不会太难。

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.

郁金香雨 2024-07-16 18:21:26

我迟到了,但你可能有兴趣调查 CHorderedDictionary。 它是 NSMutableDictionary 的子类,它封装了另一个用于维护键顺序的结构。 (它是 CHDataStructures.framework 的一部分。)我发现它比管理字典和数组更方便分别地。

披露:这是我编写的开源代码。 只是希望它对面临这个问题的其他人有用。

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.

Disclosure: This is open-source code which I wrote. Just hoping it may be useful to others facing this problem.

陪你搞怪i 2024-07-16 18:21:26

没有这样的内置方法可以让您获取它。 但一个简单的逻辑对你有用。 在准备词典时,您只需在每个键前面添加一些数字文本即可。 就像

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个字符剪掉即可。

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.

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.

你另情深 2024-07-16 18:21:26

如果你要继承 NSDictionary 的子类,你至少需要实现这些方法:

  • NSDictionary
    • -计数
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying/NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (对于 Leopard)
    • -countByEnumerateWithState:objects:count:

最简单的方法是创建一个 NSMutableDictionary 的子类,其中包含它自己操作的 NSMutableDictionary 和一个用于存储有序集的 NSMutableArray的钥匙。

如果您永远不会对对象进行编码,则可以想象跳过实现 -encodeWithCoder:-initWithCoder:

上述 10 个方法中的所有方法实现都将执行直接通过您的托管字典或有序键数组。

If you're going to subclass NSDictionary you need to implement these methods as a minimum:

  • NSDictionary
    • -count
    • -objectForKey:
    • -keyEnumerator
  • NSMutableDictionary
    • -removeObjectForKey:
    • -setObject:forKey:
  • NSCopying/NSMutableCopying
    • -copyWithZone:
    • -mutableCopyWithZone:
  • NSCoding
    • -encodeWithCoder:
    • -initWithCoder:
  • NSFastEnumeration (for 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.

If you're never going to encode your objects you could conceivable skip implementing -encodeWithCoder: and -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.

伤痕我心 2024-07-16 18:21:26

我的一点补充:按数字键排序(对较小的代码使用简写符号)

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

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]];
}
就像说晚安 2024-07-16 18:21:26

Quick 'n dirty:

当您需要订购词典(此处称为“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];
            }
        }

现在, *index 对象将包含正确的顺序。 请注意,此解决方案并不要求所有密钥都必须存在,这是我们正在处理的通常情况......

Quick 'n dirty:

When you need to order your dictionary (herein called “myDict”), do this:

     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...

撑一把青伞 2024-07-16 18:21:26

NSDictionary 有序子类的最小实现(基于 https://github.com/nicklockwood/OrderedDictionary )。 请随意扩展以满足您的需求:

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

Minimal implementation of an ordered subclass of NSDictionary (based on https://github.com/nicklockwood/OrderedDictionary). Feel free to extend for your needs:

Swift 3 and 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)
        }
    }
}

usage

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

usage

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]);
}
平安喜乐 2024-07-16 18:21:26

对于 Swift 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)

For, Swift 3.
Please try out the following approach

        //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)
烙印 2024-07-16 18:21:26

我不太喜欢 C++,但我发现自己越来越多地使用的一种解决方案是使用标准模板库中的 Objective-C++ 和 std::map。 它是一个字典,其键在插入时自动排序。 无论是标量类型还是 Objective-C 对象作为键和值,它都表现得非常好。

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

需要注意的是,您可能需要提供自己的 insert_or_assign 函数,除非您可以使用 C++17(请参阅 这个答案)。 另外,您需要typedef您的类型以防止某些构建错误。 一旦你弄清楚如何使用 std::map、迭代器等,它就会变得非常简单和快速。

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.

If you need to include an array as a value, just use std::vector instead of 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文