Obj-C,iOS,我如何按值而不是键排序,sortedArrayUsingSelector,目前@selector(compare:)]

Posted

技术标签:

【中文标题】Obj-C,iOS,我如何按值而不是键排序,sortedArrayUsingSelector,目前@selector(compare:)]【英文标题】:Obj-C, iOS, How do I sort by value and not key, sortedArrayUsingSelector, currently @selector(compare:)] 【发布时间】:2011-05-30 13:35:05 【问题描述】:

我认为我需要按值而不是键排序....

这里是我填充数组的地方

const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;

int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) 
    while(sqlite3_step(statementTMP) == SQLITE_ROW)
    
        int cid = sqlite3_column_int(statementTMP, 0);
        NSString *category = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];

        NSArray *arr=[[NSArray alloc]initWithObjects:category,nil];

        [arrayTmp setObject:arr forKey:[NSString stringWithFormat:@"%i",cid]];
        [self.cidList addObject:[NSString stringWithFormat:@"%i",cid]];

        [category release];
        [arr release];
    

sqlite3_finalize(statementTMP);
sqlite3_close(database);

self.allCategories = arrayTmp;
[arrayTmp release];

这是对数组重新排序的方法。

- (void)resetSearch 

NSMutableDictionary *allCategoriesCopy = [self.allCategories mutableDeepCopy];
self.Categories = allCategoriesCopy;
[allCategoriesCopy release];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObject:UITableViewIndexSearch];
[keyArray addObjectsFromArray:[[self.allCategories allKeys] 
                               sortedArrayUsingSelector:@selector(compare:)]];
self.keys = keyArray;
[keyArray release];

这个问题我已经有一段时间了,上次我看这个我可以找到 sortedArrayUsingSelector 比较的替代方法?

编辑

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];

NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [Categories objectForKey:key];

static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                         SectionsTableIdentifier ];
if (cell == nil) 
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                   reuseIdentifier: SectionsTableIdentifier ] autorelease];


cell.textLabel.text = [nameSection objectAtIndex:row];
return cell;


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath  

NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];

NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [Categories objectForKey:key];

NSLog(@"the selected cid is = %i",[key intValue]); 

selectButton.enabled = YES;

有人吗?

【问题讨论】:

只有受虐狂直接在 Objective-C 中使用 SQLite C API。 Use FMDB(SQLite 包装器)或CoreData(对象图管理器)。 @Dave,它对我有用,我在使用 FMDB 时遇到问题,从未尝试过 CoreData @Jules -- 您可能想详细说明您的问题是什么。您期望什么行为以及您看到的确切行为是什么。除非有明显的错误,否则很难从代码示例中准确推断出问题所在。 @TechZen 有了我的开场代码,屏幕上总是有一个表格视图、一个搜索栏和一个小键盘。加载时,tableview 会填充数据库中的所有类别。尽管这些是按字母顺序添加到主数组中的,但它们在 tableview 中显示。这是因为调用了 resetsearch,它改变了显示的内容。当用户在搜索栏中键入有限的结果时,这是由函数 handleSearchForTerm 完成的,该函数从用于显示结果的数组中删除项目。使用您提供的 resetSearch 代码,我在 tableview 中没有显示任何数据。 Cont... 我想在表格视图中按字母顺序查看结果,即使用户通过在搜索栏中输入/限制结果。 【参考方案1】:

您显然试图构造一个用于-[UITableviewDatasource sectionIndexTitlesForTableView:] 的数组。因此,您需要一个如下所示的数组(伪代码):

[UITableViewIndexSearch, 0_sectionTitle, 1_sectionTitle, 2_sectionTitle, ...]

我认为您的直接问题是您尝试将UITableViewIndexSearch 字符串常量添加到您排序的数组之前,这使得它不可能最终成为第一个元素,除非您的所有其他元素排序低于U

修复很简单,只需在排序后添加常量即可。您可以在使用时清理代码:

  NSMutableArray *secIdx=[NSMutableArray arrayWithCapacity:[[self.allCategories allKeys] count]];
  [secIdx addObjectsFromArray:[self.allCategories allKeys]];
  [secIdx sortUsingSelector:@selector(compare:)];
  [secIdx insertObject:UITableViewIndexSearch atIndex:0];
  self.keys=secIdx;

请注意,secIdx 是自动释放的,因此您不必释放它。

除了这个问题之外,您的代码还有许多不必要/危险的元素,这些元素会使您的应用变得脆弱且难以维护。

    您使用了很多 init 来处理可以使用自动释放的便捷方法的对象。 'init`s 平衡内存泄漏的风险,但没有给你任何优势。 您需要将标量值包装在对象中,以便在集合中轻松管理它们。 您正在使用不必要的数组。

你可以像这样重写第一个块:

const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;

int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) 
    NSNumber *cidNum; //... move variable declerations outside of loop
    NSString *category; //.. so they are not continously recreated
    [self.allCategories removeAllObjects]; //... clears the mutable dictionary instead of replacing it
    while(sqlite3_step(statementTMP) == SQLITE_ROW)
        cidNum=[NSNumber numberWithInt:(sqlite3_column_int(statementTMP, 0))]; 
        category=[NSString stringWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];
        //... adding the autoreleased category and cidNum to array/dictionary automatically retains them
        [self.allCategories addObject:category forKey:cidNum]; 
        [self.cidList addObject:cidNum];

        //[category release]; ... no longer needed
        //[arr release]; ... no longer needed
    

sqlite3_finalize(statementTMP);
sqlite3_close(database);

//self.allCategories = arrayTmp; ... no longer needed
//[arrayTmp release]; ... no longer needed

【讨论】:

我推断self.allCategories 是一个 NSMutableDictionary。如果它只是一个 NSDictionary,则您需要将属性更改为可变字典或使用临时可变字典然后转换为属性。我建议只使用可变字典开始,除非您希望您的列表有数千个元素。 我无法从这些小小的 sn-ps 代码中看出发生了什么。我不知道什么变量/属性包含什么数据以及为什么。如果你愿意,你可以发布一个指向 header(.h) 和 implementation(.m) 文件的链接,我可以看看。或者,您可以通过电子邮件将它们发送给我(单击徽章中的我的名字以获取电子邮件。) @Jules -- 我没有收到任何东西。这是我 dot com 的技术。 @Jules -- 知道了,但请看我的依赖【参考方案2】:

使用-sortedArrayUsingComparator:(如果不能使用块,则使用-sortedArrayUsingFunction:context:)。示例:

NSDictionary *categories = [self allCategories];
NSArray *keysSortedByValue = [[categories allKeys] sortedArrayUsingComparator:
^(id left, id right) 
    id lval = [categories objectForKey:left];
    id rval = [categories objectForKey:right];
    return [lval compare:rval];
];

【讨论】:

@Jules 请更准确。它以什么方式无法工作?是否有任何错误信息?如果有,每条错误信息的全文是什么? @Jules 这只是一个可变性问题。您需要将 Obj-C 转换为可变实例的等效项:[[keysSortedByValue mutableCopy] autorelease],或使用屏幕截图中的变量self.keys = [[keyArray mutableCopy] autorelease] @Jules 或者(更清洁)[[self keys] setArray:allKeys]。这只是将当前keys 数组中的键替换为allKeys 中的所有键,这意味着您可以跳过取消分配旧keys 数组并复制新数组。好多了。 @Jules 将-compare: 消息替换为将比较两个数组并返回一个NSComparisonResult 指定它们的顺序的任何消息发送或函数调用。我假设您有一个将标量键映射到标量值的字典,并希望按值的排序顺序对键进行排序。大多数基本的 Foundation 类型都响应 compare:,这就是我在示例代码中使用的,但同样的想法是使用 -sortedArrayUsingComparator: 并使用值而不是键。 我没有比较功能,不知道如何继续帮助【参考方案3】:

您可以创建一个小型模型类 Category 并在其中实现比较,然后使用该比较对这些对象的数组进行排序:。

这里有一些信息 - How to sort an NSMutableArray with custom objects in it?

【讨论】:

我不确定如何实现这个?【参考方案4】:

也许您正在寻找 NSSortDescriptor(以及相应的排序方法,-[NSArray sortedArrayUsingDescriptors])和朋友?

【讨论】:

【参考方案5】:

如果我理解正确,那么您希望从数据库中获取类别并将其显示在 tableView 上,按字母排序,右侧索引和顶部搜索栏。理想情况下,您希望显示联系人应用程序类型的视图。如果正确,请使用以下代码从数据库中获取项目并重建(或重置)它 -

const char *sql = "select cid, category from Categories ORDER BY category DESC";
sqlite3_stmt *statementTMP;

NSMutableArray *arrayTmp = [[NSMutableArray alloc] init];

int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) 
    while(sqlite3_step(statementTMP) == SQLITE_ROW) 
         int cid = sqlite3_column_int(statementTMP, 0);
         NSString *category = [[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(statementTMP, 1)];

         NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
         [dict setObject:category forKey:@"Category"];
         [dict setObject:[NSNumber numberWithInt:cid] forKey:@"CID"];

         [arrayTmp addObject:dict];
         [dict release];
         [category release];
    


sqlite3_finalize(statementTMP);
sqlite3_close(database);

self.allCategories = arrayTmp;
[arrayTmp release];

然后使用这个函数重建项目 -

- (void)rebuildItems 
    NSMutableDictionary *map = [NSMutableDictionary dictionary];

    for (int i = 0; i < allCategories.count; i++) 
        NSString *name = [[allCategories objectAtIndex:i] objectForKey:@"Category"];
        NSString *letter = [name substringToIndex:1];
        letter = [letter uppercaseString];

        if (isdigit([letter characterAtIndex:0]))
              letter = @"#";

        NSMutableArray *section = [map objectForKey:letter];
        if (!section) 
            section = [NSMutableArray array];
            [map setObject:section forKey:letter];
        
        [section addObject:[allCategories objectAtIndex:i]];
    

    [_items release];
    _items = [[NSMutableArray alloc] init];
    [_sections release];
    _sections = [[NSMutableArray alloc] init];

    NSArray* letters = [map.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    for (NSString* letter in letters) 
        NSArray* items = [map objectForKey:letter];
        [_sections addObject:letter];
        [_items addObject:items];
    

现在,在 tableView 中显示项目,使用以下方法 -

#pragma mark -
#pragma mark Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView 
    if (_sections.count)
        return _sections.count;
    else
        return 1;


- (NSInteger)tableView:(UITableView*)tableView sectionForSectionIndexTitle:(NSString *)title
               atIndex:(NSInteger)index 

    if (tableView.tableHeaderView) 
        if (index == 0) 
            [tableView scrollRectToVisible:tableView.tableHeaderView.bounds animated:NO];
            return -1;
        
    

    NSString* letter = [title substringToIndex:1];
    NSInteger sectionCount = [tableView numberOfSections];
    for (NSInteger i = 0; i < sectionCount; i++) 
         NSString* section = [tableView.dataSource tableView:tableView titleForHeaderInSection:i];
         if ([section hasPrefix:letter]) 
             return i;
         
    

    if (index >= sectionCount) 
        return sectionCount-1;
     else 
        return index;
    



- (NSArray*)lettersForSectionsWithSearch:(BOOL)withSearch withCount:(BOOL)withCount 
    if (isSearching)
        return nil;

    if (_sections.count) 
        NSMutableArray* titles = [NSMutableArray array];
        if (withSearch) 
            [titles addObject:UITableViewIndexSearch];
        
        for (NSString* label in _sections) 
            if (label.length) 
                NSString* letter = [label substringToIndex:1];
                [titles addObject:letter];
            
        
        if (withCount) 
            [titles addObject:@"#"];
        
        return titles;
     else 
        return nil;
    



- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 
    return [self lettersForSectionsWithSearch:YES withCount:NO];



- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section 
    if (_sections.count) 
        NSArray* items = [_items objectAtIndex:section];
        return items.count;
     else 
        return _items.count;
    



- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
    if (_sections.count) 
        return [_sections objectAtIndex:section];

    return nil;



- (id)tableView:(UITableView *)tableView objectForRowAtIndexPath:(NSIndexPath *)indexPath 
    if (_sections.count) 
        NSArray *section = [_items objectAtIndex:indexPath.section];
        return [section objectAtIndex:indexPath.row];
     else 
        return [_items objectAtIndex:indexPath.row];
    



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

    // Create your UITableViewCell.

    // Configure the cell.
    NSDictionary *dict = [self tableView:tableView objectForRowAtIndexPath:indexPath];
    cell.textLabel.text = [dict objectForKey:@"Category"];
            cell.detailTextLabel.text = [NSString stringWithFormat:%d, [[dict objectForKey:@"CID"] intValue]];
    return cell;


#pragma mark -
#pragma mark Table view delegate


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 
    if (isSearching)
        return nil;

    NSString *title = @"";
    if (_sections.count)       
        title = [[_sections objectAtIndex:section] substringToIndex:1];
     else 
        return nil;
    

    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 20)];
    view.backgroundColor = [UIColor colorWithRed:(58/255.0) green:(27/255.0) blue:(6/255.0) alpha:1.0];

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 1, 50, 18)];
    label.textColor = [UIColor whiteColor];
    label.backgroundColor = [UIColor clearColor];
    label.font = [UIFont boldSystemFontOfSize:17.0];
    label.text = title;

    [view addSubview:label];
    [label release];

    return [view autorelease];


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    NSDictionary *dict = [self tableView:tableView objectForRowAtIndexPath:indexPath];

    NSLog(@"selected row id:%d, name:%@", [dict objectForKey:@"Category"], [[dict objectForKey:@"CID"] intValue]);

剩下的部分是实现 UISearchBarDelegate 和实现 tableView 的搜索,可以使用下面的代码来完成:

- (void)searchBar:(UISearchBar *)searchbar textDidChange:(NSString *)searchText 

    [_sections removeAllObjects];
    [_items removeAllObjects];

    if([searchText isEqualToString:@""] || searchText == nil) 
        [self rebuildItems];
        return;
    

    NSInteger counter = 0;
    for(NSDictionary *dict in allCategories) 
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSRange r = [[dict objectForKey:@"Category"] rangeOfString:searchText options:NSCaseInsensitiveSearch];
        if(r.location != NSNotFound) 
            if(r.location == 0) 
                [_items addObject:dict];
            
        
        counter++;
        [pool release];
    

    [contactList reloadData];

希望这就是你要找的。​​p>

【讨论】:

我从你的代码中得到了很多错误,这里有一些错误......'NSMutableDictionary'可能不响应'-addObject:','NSDictionary'可能不响应'-objectAtIndex :', '_items' 未声明(在此函数中首次使用), '_sections' 未声明(在此函数中首次使用), 'isSearching' 未声明(在此函数中首次使用),错误:'cell' 未声明(在此函数中首次使用)函数),可能不响应'-refreshContactsList',错误:'allContacts'未声明(在此函数中首次使用),错误:'contactList'未声明(在此函数中首次使用) 我认为_sections & _items 是 NSMutableArray 类型的全局对象。并且还将contactList 替换为您的UITableView 的对象。就像我们从您的代码 sn-p 中了解到的那样,allCategories 将是一个属性对象并且必须已被合成。 NSMutableArray *arrayTmp = [[NSMutableArray alloc] init];已经在开始的代码中定义了。另外,修复 refreshContactsList 和 allContacts 方法调用的错误,并用适当的调用替换它。 isSearching 又是一个全局布尔值。从 UISearchBar 开始搜索时应该设置它。 另外,我确实提到了 // 创建你的 UITableViewCell 的评论。即按照标准方法创建 UITableViewCell *cell。 我不明白你为什么/什么要求这个。 NSMutableArray *arrayTmp = [[NSMutableArray alloc] init]; - 自从我第一次回答以来添加了行。您可以从编辑历史中查看。【参考方案6】:

在你的排序功能上你应该试试这个:

NSArray *cntxt; //im not sure this is the correct type that ur using on keyArray
[keyArray addObjectsFromArray:[self.allCategories allKeys]];
[keyArray sortUsingFunction:compareFunction context:cntxt];

以及您根据需要修改的比较功能

NSInteger compareFunction(id x, id y, void *context) 
    //NSArray *ctxt = context;
    NSArray *c1 = x;
    NSArray *c2 = y;

    if ([c1 value] < [c2 value])
        return NSOrderedDescending;
    else if ([c1 value] > [c2 value])
        return NSOrderedAscending;
    else 
        return NSOrderedSame;

编辑:阅读您的 cmets 并重新查看您的代码后,您的 keyArray 似乎是 NSString 类型的对象,因此您应该更改:

NSInteger compareFunction(id x, id y, void *context) 
    //NSString *ctxt = context;
    NSString *c1 = x;
    NSString *c2 = y;
    NSComparisonResult result;
    result = [c1 compare:c2];

    if (result<0)
        return NSOrderedAscending;
    else if (result>0)
        return NSOrderedDescending;
    else 
        return NSOrderedSame;

【讨论】:

谢谢@iruleonu,我收到了一些警告NSArray may not respond to 'value'。我真的希望这有效:) 我写的 [c1 value] 仅作为示例。此比较将取决于您的 NSArray keyArray 包含的对象的类型。 @iruleonu 好的,这会编译并运行,但不会生成按字母顺序排列的值列表。也许您对外键 ID 号进行排序? @iruleonu 是的,这似乎是skitch.com/mindwarpltd/fdmua/… 嗯,这应该可以工作,将 keyArray 内容的键更改为值的行,[keyArray addObjectsFromArray:[self.allCategories allValues]];

以上是关于Obj-C,iOS,我如何按值而不是键排序,sortedArrayUsingSelector,目前@selector(compare:)]的主要内容,如果未能解决你的问题,请参考以下文章

如何按值而不是按引用复制对象[重复]

在 Python 2 中,无论列表的内容如何,​​如何按值而不是引用复制复杂嵌套元素的列表

Clojure按值获取映射键

按值而不是按引用复制对象

按值而不是引用将 List<T> 分配给另一个 List<T> [重复]

Firebase 实时数据库:如何在按值排序后围绕特定键检索 10 条记录?