정렬 된 키가있는 NSDictionary


81

기본적으로 연관 배열 (키와 값으로 문자열)로 사용하는 NSDictionary (plist에 저장 됨)가 있습니다. 응용 프로그램의 일부로 키 배열을 사용하고 싶지만 특정 순서 (정렬 할 알고리즘을 작성할 수있는 순서가 아님)를 원합니다. 나는 항상 별도의 키 배열을 저장할 수 있지만, 항상 사전의 키와 배열의 값을 업데이트하고 항상 일치하는지 확인해야했기 때문에 다소 까다로워 보입니다. 현재는 [myDictionary allKeys] 만 사용하고 있지만 분명히 임의의 비 보장 순서로 반환합니다. Objective-C에 누락 된 데이터 구조가 있습니까? 누구든지 이것을 더 우아하게 수행하는 방법에 대한 제안이 있습니까?

답변:


23

관련된 NSMutableArray 키를 갖는 솔루션은 그리 나쁘지 않습니다. NSDictionary의 서브 클래 싱을 피하고 접근 자 작성에주의를 기울이면 동기화를 유지하는 것이 너무 어렵지 않아야합니다.


2
한 가지 함정은 NS (Mutable) Dictionary가 키의 복사본을 만들기 때문에 키 배열의 개체가 달라진다는 것입니다. 키가 변경되면 문제가 될 수 있습니다.
Quinn Taylor

1
그것은 좋은 점이지만 가변성이 배열 내의 항목을 변경하는 대신 배열에서 객체를 추가하고 제거하는 것으로 제한되는 경우 완화됩니다.
Abizern 2009-06-20

1
처음에 포인터를 제공 한 키를 수정하는 사람을 어떻게 제한할지 확신 할 수 없습니다. 배열은 해당 키에 대한 포인터 만 저장하고 변경 여부를 알 수 없습니다. 키가 변경되면 배열에서 추가 된 항목을 전혀 제거하지 못할 수도 있습니다. 이는 배열이 더 이상 사전에없는 키를 유지하는 경우 실제 동기화 문제가 될 수 있으며 그 반대의 경우도 마찬가지입니다 ... : -)
Quinn Taylor

2
BTW, 내가 지나치게 비판적으로 들리면 사과드립니다. 나는이 같은 문제를 직접 다루고 있지만 프레임 워크를위한 것이기 때문에 가능한 모든 상황에서 견고하고 정확하도록 설계하려고 노력하고 있습니다. 별도의 배열이 그렇게 나쁘지 않다는 것은 맞지만, 그것을 추적하는 클래스만큼 간단하지는 않습니다. 또한 이러한 클래스가 NS (Mutable) Dictionary를 확장하면 Cocoa 사전이 필요한 모든 곳에서 사용할 수 있으므로 많은 이점이 있습니다.
Quinn Taylor

1
나는 토론에 신경 쓰지 않는다. 내 제안은 해결 방법 일뿐입니다. 프레임 워크를 작성하는 것은 완전히 다른 명제이며 신중한 생각이 필요합니다. 개인적으로 나는 바로 이런 이유로 인해 변경 가능성이 번거 롭다는 것을 알게되었고, 내 프로그램에서 이러한 클래스의 사용을 최소화하려고 노력합니다.
Abizern 2009-06-20

19

실제 답변으로 게임에 늦었지만 CHOrderedDictionary 조사에 관심이있을 수 있습니다 . 키 순서를 유지하기 위해 다른 구조를 캡슐화하는 NSMutableDictionary의 하위 클래스입니다. ( CHDataStructures.framework 의 일부입니다 .) 딕셔너리와 배열을 별도로 관리하는 것보다 더 편리하다고 생각합니다.

공개 : 이것은 내가 작성한 오픈 소스 코드입니다. 이 문제에 직면 한 다른 사람들에게 유용 할 수 있기를 바랍니다.


1
+1-아직 시도하지 않았지만 문서를 살펴 보았으며 여기에서 멋진 작업을 한 것 같습니다. 나는 애플이 이러한 기본 요소를 너무 많이 놓치고 있다는 것이 조금 당황 스럽다고 생각한다.
whitneyland

나는 그것이 창피하다고 생각하지 않고 단지 프레임 워크가 어떻게 개발되기를 원하는지에 대한 질문 일뿐입니다. 특히, 새로운 클래스는 재단에 포함시키기 위해 많은 가치를 추가해야합니다. 사전을 정렬 된 키 배열과 쌍으로 연결하는 것은 그리 복잡하지 않으며, Apple은 일부 사람들이 사용할 수있는 항목을 지나치게 사용하지 않음으로써 프레임 워크의 크기 (및 유지 관리 가능성)를 포함하려고합니다.
Quinn Taylor

3
정렬 된 사전이 누락 된 것이 아니라 유용한 CS101 컬렉션이 너무 많이 누락되어 있기 때문입니다. C, C ++, Java, C #과는 달리 Objective-C는 Apple 없이는 널리 성공적인 언어가 될 것이라고 생각하지 않습니다. C, C ++, Java, C #은 제작자의 범위 밖에서 여러 번 성공적으로 입증되었습니다.
whitneyland

1
이. 이다. 대박. 지금까지이 프레임 워크를 발견하지 못한 것이 아쉽습니다. 이것은 대부분의 향후 프로젝트에서 중심이 될 것입니다. 고마워요!
Dev Kanchen 2012

키가 NSString이면 다음을 수행하는 것이 문제입니까? 사전과 배열을 만들고 데이터를 추가하고 제거하는 일반적인 방법을 사용합니다. 배열이 사전이있는 NSString 키와 값을 포함 (원하는 순서대로)는 NSString 키의 목록을 포함
user1046037

15

이것을 얻을 수있는 내장 된 방법은 없습니다. 그러나 간단한 논리가 작동합니다. 사전을 준비하는 동안 각 키 앞에 숫자 텍스트를 몇 개만 추가하면됩니다. 처럼

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 개 문자를 잘라냅니다.


이것은 처음 세 문자를 제거하는 방법을 보여줍니다. stackoverflow.com/questions/1073872/…
Leon

7

NSDictionary를 하위 클래스로 만들려면 최소한 다음 메서드를 구현해야합니다.

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

원하는 작업을 수행하는 가장 쉬운 방법은 조작하는 자체 NSMutableDictionary와 정렬 된 키 집합을 저장하는 NSMutableArray를 포함하는 NSMutableDictionary의 하위 클래스를 만드는 것입니다.

당신이 당신의 객체를 인코딩하기 위해가는 일은 없습니다 경우 구현 건너 생각할 수 -encodeWithCoder:-initWithCoder:

위의 10 개 메서드의 모든 메서드 구현은 호스팅 된 사전 또는 순서가 지정된 키 배열을 통해 직접 이동합니다.


5
한 가지 더 분명하지 않고 나를 넘어 뜨린 것이 있습니다. NSCoding을 구현하는 경우-(Class) classForKeyedArchiver를 재정 의하여 [self class]를 반환하십시오. 클래스 클러스터는 항상 추상 부모 클래스를 반환하도록 설정하므로 변경하지 않는 한 인스턴스는 항상 NS (Mutable) Dictionary로 디코딩됩니다.
Quinn Taylor

5

내 작은 추가 사항 : 숫자 키로 정렬하기 (작은 코드에 단축 표기법 사용)

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

빠르고 더러운 :

사전 (이하 "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 객체는 올바른 순서로 적절한 키를 포함합니다. 이 솔루션은 모든 키가 반드시 존재해야하는 것은 아니며, 우리가 다루는 일반적인 상황입니다.


배열 순서에 대한 sortedArrayUsingSelector는 어떻습니까? developer.apple.com/library/ios/documentation/cocoa/Conceptual/...
알렉스 Zavatone

2

For, 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)

2

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

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

0

저는 C ++를별로 좋아하지 않지만 제가 점점 더 많이 사용하고있는 한 가지 해결책은 Objective-C ++와 std::map표준 템플릿 라이브러리를 사용하는 것입니다. 삽입시 키가 자동으로 정렬되는 사전입니다. 스칼라 유형이나 Objective-C 객체 모두 키와 값으로 놀랍도록 잘 작동합니다.

배열을 값으로 포함해야하는 경우 std::vector대신을 사용하십시오 NSArray.

한 가지주의 insert_or_assign할 점은 C ++ 17을 사용할 수 없다면 자체 함수 를 제공하고 싶을 수도 있다는 것입니다 ( 이 답변 참조 ). 또한 typedef특정 빌드 오류를 방지하기 위해 유형 이 필요 합니다. std::map, iterators 등 을 사용하는 방법을 알아 내면 매우 간단하고 빠릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.