具有多个约束的 NSDictionary 排序

发布于 2024-09-25 18:49:40 字数 825 浏览 5 评论 0原文

我有一个 NSDictionary 集合,其键是唯一的 id,值是一个包含两个不同对象(FruitClass、ProductClass)的数组,我想对集合进行分组,使其首先按 ProductClass.productName 排序,然后按 FruitClass.itemName 排序。

因此,最终列表将类似于:

{apple, butter}
{apple, pie}
{banana, daiquiri}
{banana, smoothie}
{melon, zinger}

其中第一项是 FruitClass 实例项,第二项是 ProductClass 实例项。

执行此操作的最佳方法是什么?我遇到的大多数示例都是通过一键完成的。如何使用具有 2 种不同对象类型的 NSDictionary 来做到这一点?

看看 NSDictionary 的 keysSortedByValueUsingSelector,

- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator

我的印象是您将在值对象的类类型上创建“比较”方法。因此,对于多字段排序,我是否必须创建一个新的对象类型“CombinedClass”,其中包含 FruitClass 和“CombinedClass”? ProductClass 并实现“比较”来实现这一点?

FruitClass:
{
    NSString *itemName;
}
@end
@interface ProductClass
{
    NSString *productName;
}
@end

I have a NSDictionary collection whose key is a unique id and value is an array with two different objects (FruitClass, ProductClass) and I would like to group the collection such that it's sorted first by ProductClass.productName and then by FruitClass.itemName.

So the final list would look something like:

{apple, butter}
{apple, pie}
{banana, daiquiri}
{banana, smoothie}
{melon, zinger}

where the first item is a FruitClass instance item and second is a ProductClass instance item.

What's the best way to go about doing this? Most of the examples I've come across are done on one key. How do you do it with an NSDictionary that has 2 different object types?

Looking at NSDictionary's's keysSortedByValueUsingSelector,

- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator

I get the impression that you would create the 'compare' method on the class type of the value object. So for multiple field sort, would I have to resort to creating a new object type, 'CombinedClass' which contains FruitClass & ProductClass and implement a 'compare' to make this happen?

FruitClass:
{
    NSString *itemName;
}
@end
@interface ProductClass
{
    NSString *productName;
}
@end

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

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

发布评论

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

评论(3

素手挽清风 2024-10-02 18:49:40

如果存在仅包含一种水果和一种产品的数据结构,那么数组并不是一个好的选择。您可以使用另一个类并提供 compare: 比较器:

@interface ComboClass : NSObject
{
    FruitClass *fruit;
    ProductClass *product;
}

@property(nonatomic,retain) FruitClass *fruit;
@property(nonatomic,retain) ProductClass *product;

- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p;

@end


@implementation ComboClass

@synthesize fruit;
@synthesize product;

- (void) dealloc
{
    [fruit release];
    [product release];
    [super dealloc];
}

- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p
{
    self = [super init];
    if (!self) return nil;

    self.fruit = f;   // some recommend against accessor usage in -init methods
    self.product = p;

    return self;
}

- (NSComparisonResult) compare:(id) another
{
    NSComparisonResult result = [self.fruit.itemName compare:another.fruit.itemName];
    if (result == NSOrderedSame)
        return [self.product.productName compare:another.product.productName];
    else
        return result;
}

@end

或者,您也可以使用带有 productfruit 键值的 NSDictionary对,(所以你最终会在字典中得到字典)。 NSSortDescriptor< /code>类可用于使用键路径的值对数组进行排序,因此它可能是另一个值得探索的选项。

If there is a data structure that consists of only one fruit and only one product then an array is not really a good option. You can use another class and provide a compare: comparator:

@interface ComboClass : NSObject
{
    FruitClass *fruit;
    ProductClass *product;
}

@property(nonatomic,retain) FruitClass *fruit;
@property(nonatomic,retain) ProductClass *product;

- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p;

@end


@implementation ComboClass

@synthesize fruit;
@synthesize product;

- (void) dealloc
{
    [fruit release];
    [product release];
    [super dealloc];
}

- initWithFruit:(FruitClass *)f andProduct:(ProductClass *) p
{
    self = [super init];
    if (!self) return nil;

    self.fruit = f;   // some recommend against accessor usage in -init methods
    self.product = p;

    return self;
}

- (NSComparisonResult) compare:(id) another
{
    NSComparisonResult result = [self.fruit.itemName compare:another.fruit.itemName];
    if (result == NSOrderedSame)
        return [self.product.productName compare:another.product.productName];
    else
        return result;
}

@end

Alternatively, you might be able to use an NSDictionary with product and fruit key-value pairs, (so you'll end up with dictionaries inside a dictionary). The NSSortDescriptor class can be used to sort arrays using values of key-paths, so it might be another option to explore.

半步萧音过轻尘 2024-10-02 18:49:40

您可以向 NSArray 添加一个类别来进行比较,而不必创建另一个类。

@interface NSArray ( MySortCategory )
    - (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
@end

根据您的描述,实现应该非常简单。

编辑 我对它被标记下来而没有任何评论感到有点恼火,所以我做了一个完整的实现以确保它能够工作。这与您的示例略有不同,但想法相同。

FruitsAndProducts.h

@interface Fruit : NSObject
{
    NSString *itemName;
}

@property(nonatomic, copy)NSString *itemName;

@end

@interface Product : NSObject
{
    NSString *productName;
}

@property(nonatomic, copy)NSString *productName;

@end

FruitsAndProducts.m

#import "FruitsAndProducts.h"

@implementation Fruit

@synthesize itemName;

@end


@implementation Product

@synthesize productName;

@end

NSArray+MyCustomSort.h

@interface NSArray (MyCustomSort)
- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
@end

NSArray+MyCustomSort.m

#import "NSArray+MyCustomSort.h"

#import "FruitsAndProducts.h"

@implementation NSArray (MyCustomSort)

- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare
{
    // This sorts by product first, then fruit.
    Product *myProduct = [self objectAtIndex:0];
    Product *productToCompare = [arrayToCompare objectAtIndex:0];

    NSComparisonResult result = [myProduct.productName caseInsensitiveCompare:productToCompare.productName];
    if (result != NSOrderedSame) {
        return result;
    }

    Fruit *myFruit = [self objectAtIndex:1];
    Fruit *fruitToCompare = [arrayToCompare objectAtIndex:1];

    return [myFruit.itemName caseInsensitiveCompare:fruitToCompare.itemName];
}


@end

这里是在行动

// Create some fruit.
Fruit *apple = [[[Fruit alloc] init] autorelease];
apple.itemName = @"apple";
Fruit *banana = [[[Fruit alloc] init] autorelease];
banana.itemName = @"banana";
Fruit *melon = [[[Fruit alloc] init] autorelease];
melon.itemName = @"melon";

// Create some products
Product *butter = [[[Product alloc] init] autorelease];
butter.productName = @"butter";
Product *pie = [[[Product alloc] init] autorelease];
pie.productName = @"pie";
Product *zinger = [[[Product alloc] init] autorelease];
zinger.productName = @"zinger";

// create the dictionary. The array has the product first, then the fruit.
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObjects:zinger, banana, nil], @"zinger banana", [NSArray arrayWithObjects:butter, apple, nil], @"butter apple", [NSArray arrayWithObjects:pie, melon, nil], @"pie melon", nil];

NSArray *sortedKeys = [myDict keysSortedByValueUsingSelector:@selector(customCompareToArray:)];

for (id key in sortedKeys) {
    NSLog(@"key: %@", key);
}

You could add a category to NSArray that would do the comparison and you wouldn't have to create another class.

@interface NSArray ( MySortCategory )
    - (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
@end

The implementation should be pretty straightforward based on your description.

Edit I got a little miffed that this was marked down with no comment, so I did a full implementation to make sure it would work. This is a little different than your sample, but the same idea.

FruitsAndProducts.h

@interface Fruit : NSObject
{
    NSString *itemName;
}

@property(nonatomic, copy)NSString *itemName;

@end

@interface Product : NSObject
{
    NSString *productName;
}

@property(nonatomic, copy)NSString *productName;

@end

FruitsAndProducts.m

#import "FruitsAndProducts.h"

@implementation Fruit

@synthesize itemName;

@end


@implementation Product

@synthesize productName;

@end

NSArray+MyCustomSort.h

@interface NSArray (MyCustomSort)
- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare;
@end

NSArray+MyCustomSort.m

#import "NSArray+MyCustomSort.h"

#import "FruitsAndProducts.h"

@implementation NSArray (MyCustomSort)

- (NSComparisonResult)customCompareToArray:(NSArray *)arrayToCompare
{
    // This sorts by product first, then fruit.
    Product *myProduct = [self objectAtIndex:0];
    Product *productToCompare = [arrayToCompare objectAtIndex:0];

    NSComparisonResult result = [myProduct.productName caseInsensitiveCompare:productToCompare.productName];
    if (result != NSOrderedSame) {
        return result;
    }

    Fruit *myFruit = [self objectAtIndex:1];
    Fruit *fruitToCompare = [arrayToCompare objectAtIndex:1];

    return [myFruit.itemName caseInsensitiveCompare:fruitToCompare.itemName];
}


@end

Here it is in action

// Create some fruit.
Fruit *apple = [[[Fruit alloc] init] autorelease];
apple.itemName = @"apple";
Fruit *banana = [[[Fruit alloc] init] autorelease];
banana.itemName = @"banana";
Fruit *melon = [[[Fruit alloc] init] autorelease];
melon.itemName = @"melon";

// Create some products
Product *butter = [[[Product alloc] init] autorelease];
butter.productName = @"butter";
Product *pie = [[[Product alloc] init] autorelease];
pie.productName = @"pie";
Product *zinger = [[[Product alloc] init] autorelease];
zinger.productName = @"zinger";

// create the dictionary. The array has the product first, then the fruit.
NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObjects:zinger, banana, nil], @"zinger banana", [NSArray arrayWithObjects:butter, apple, nil], @"butter apple", [NSArray arrayWithObjects:pie, melon, nil], @"pie melon", nil];

NSArray *sortedKeys = [myDict keysSortedByValueUsingSelector:@selector(customCompareToArray:)];

for (id key in sortedKeys) {
    NSLog(@"key: %@", key);
}
递刀给你 2024-10-02 18:49:40

你的比较器可以处理你扔给它的任何东西...你可以让它把它的 two 参数 s 视为 NSArray 对象s,如果这样就是你所需要的。当您将数组作为值放入字典中时,只需使用它们即可 - 不需要另一个类。

如果你无论如何都想构建一个新类(也许是出于设计原因) - 那就去做吧,但这不是这里的“必须做”。

编辑:删除以明确仅给出一个参数 - 因为另一个参数是调用选择器的对象。使用 NSArray 需要一个类扩展,自定义类更简洁。

Your comparator can work on whatever you throw at it... you can make it treat its two arguments as NSArray objects, if this is what you need. When you put arrays as values into your dictionary, then just use those - no need for another class.

If you want to build a new class anyway (maybe for design reasons) - go for it, but it is not a "must do" here.

Edit: strike out to make clear that only one argument is given - as the other one is the object the selector is called on. Using NSArray will need a class extension, a custom class is much cleaner.

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