如何对 NSString 中的成分进行排序?前任。 1/2杯牛奶,1/4勺粉

Posted

技术标签:

【中文标题】如何对 NSString 中的成分进行排序?前任。 1/2杯牛奶,1/4勺粉【英文标题】:How to sort ingredients inside NSString? Ex. 1/2 Cup Milk, 1/4 Spoon Powder 【发布时间】:2016-10-03 18:51:34 【问题描述】:

我在 NSArray 中有如下成分列表作为 NSString 对象。

"1/2 Cup Milk",
"125 grams Cashew",
"2 green onions",
"1/4 Cup Sugar",
"1/6 Spoon Salt",
"3/2 XYZ",
"One cup water",
"Almond oil"

我想像下面这样对它们进行排序。

"1/6 Spoon Salt",
"1/4 Cup Sugar",
"1/2 Cup Milk",
"3/2 XYZ",
"2 green onions",
"125 grams Cashew",
"Almond oil",
"One cup water",

尝试 1: 使用localizedStandardCompare 和 NSNumericSearch 进行排序,结果都相同。

结果:

"1/2 Cup Milk",
"1/4 Cup Sugar",
"1/6 Spoon Salt",
"2 green onions",
"3/2 XYZ",
"125 grams Cashew",
"Almond oil",
"One cup water"

我知道这是可能的,但不知何故我无法弄清楚。

如果有人做过类似的事情,你可以指导我。

谢谢,提前。

【问题讨论】:

您是否尝试过对字符串数组进行数字排序(有很多示例说明如何)?我不知道它是否能正确处理分数。 请参阅 ***.com/questions/2846301/… 并告诉我们它是否适用于您的分数。 @rmaddy 好的,让我试试这个。 @rmaddy 遗憾的是,不,它不处理分数。它也不能处理“One cup”(尽管听起来他并不希望将其排序为“1”,无论如何)。 是的@Rob 我不希望对 One Cup 进行排序,但如果它可以做到,那就太好了。 【参考方案1】:

您可以在NSString 上定义一个类别来比较数字(包括分数):

@interface NSString (Number)
- (NSNumber * _Nullable)numberValue;
- (NSComparisonResult)compareNumber:(NSString *)string;
@end

@implementation NSString (Number)

- (NSNumber *)numberValue 
    NSError *error;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\s*(\\d+)\\s*/\\s*(\\d+)" options:0 error:&error];
    NSAssert(regex, @"%@", error.localizedDescription);

    NSTextCheckingResult *match = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];
    if (match) 
        float numerator = [[self substringWithRange:[match rangeAtIndex:1]] floatValue];
        float denominator = [[self substringWithRange:[match rangeAtIndex:2]] floatValue];
        return denominator ? @(numerator / denominator) : nil;
    

    regex = [NSRegularExpression regularExpressionWithPattern:@"^\\s*(\\d+)" options:0 error:&error];
    match = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];
    if (match) 
        return @([self floatValue]);
    

    // if you don't want it to recognize spelt out numbers, comment the following bit of code

    regex = [NSRegularExpression regularExpressionWithPattern:@"^\\s*(\\S+)" options:0 error:&error];
    match = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];
    if (match) 
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        formatter.numberStyle = NSNumberFormatterSpellOutStyle;
        return [formatter numberFromString:[[self substringWithRange:[match rangeAtIndex:1]] lowercaseString]];
    

    return nil;


- (NSComparisonResult)compareNumber:(NSString *)string 
    NSNumber *number1 = [self numberValue];
    NSNumber *number2 = [string numberValue];

    if (number1 && !number2) 
        return NSOrderedAscending;
    

    if (number2 && !number1) 
        return NSOrderedDescending;
    

    if (number1 && number2) 
        NSComparisonResult numericComparison = [number1 compare:number2];
        if (numericComparison != NSOrderedSame) 
            return numericComparison;
        
    

    return [self caseInsensitiveCompare:string];


@end

然后您可以像这样对成分进行排序:

NSArray *ingredients = @[@"1/2 Cup Milk",
                         @"125 grams Cashew",
                         @"2 green onions",
                         @"1/4 Cup Sugar",
                         @"1/6 Spoon Salt",
                         @"3/2 XYZ",
                         @"One cup water",
                         @"Almond oil"];

NSArray *sorted = [ingredients sortedArrayUsingComparator:^NSComparisonResult(NSString  * _Nonnull obj1, NSString  * _Nonnull obj2) 
    return [obj1 compareNumber:obj2];
];

产生:

"1/6 Spoon Salt",
"1/4 Cup Sugar",
"1/2 Cup Milk",
"One cup water",
"3/2 XYZ",
"2 green onions",
"125 grams Cashew",
"Almond oil"

我必须承认,用于拼出数字的NSNumberFormatter 逻辑并不可靠(它识别“三十二”,但不识别“三十二”),但如果您愿意,可以使用它。或者你可能会完全放弃它。

【讨论】:

呃!我刚刚花了最后 20 分钟编写基本相同的代码,只是为了在这里找到你的答案。 这是一个拖累。对不起:( 没问题。这是一个有趣的小消遣。我还是发布了我的,以显示略有不同。 @Rob 和 rmaddy 首先感谢您的努力,你们都有正确的答案我希望我能接受。我将接受抢劫回答的那个。【参考方案2】:

似乎 Rob 在我忙着写自己的答案时发布了他的好答案。既然我花了时间,我不妨张贴我的。它们相似但不同。

我也开始扩展NSString

@interface NSString (NumberSort)

- (nullable NSNumber *)leadingNumber;

@end

@implementation NSString (NumberSort)

- (nullable NSNumber *)leadingNumber 
    if (self.length < 1) return nil;

    // See if the string starts with a digit
    unichar first = [self characterAtIndex:0];
    if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:first]) 
        // It does so now get the first word (number)
        NSString *numStr = self;
        NSRange spaceRange = [self rangeOfString:@" "];
        if (spaceRange.location != NSNotFound) 
            numStr = [self substringToIndex:spaceRange.location];
        

        // Now see if the leading number is actually a fraction
        NSRange slashRange = [numStr rangeOfString:@"/"];
        if (slashRange.location != NSNotFound) 
            // It's a fraction. Compute its value
            NSString *numeratorStr = [[numStr substringToIndex:slashRange.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
            NSString *denominatorStr = [[numStr substringFromIndex:slashRange.location + slashRange.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

            NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
            fmt.numberStyle = NSNumberFormatterDecimalStyle;
            NSNumber *numerator = [fmt numberFromString:numeratorStr];
            NSNumber *denominator = [fmt numberFromString:denominatorStr];
            if (numerator && denominator) 
                return @([numerator doubleValue] / [denominator doubleValue]);
            
         else 
            // Not a fraction, convert number string to number
            NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
            fmt.numberStyle = NSNumberFormatterDecimalStyle;
            NSNumber *num = [fmt numberFromString:numStr];

            return num;
        
     else 
        // See if string starts with spelled out number
        NSString *numStr = self;
        NSRange spaceRange = [self rangeOfString:@" "];
        if (spaceRange.location != NSNotFound) 
            numStr = [self substringToIndex:spaceRange.location];
        

        NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
        fmt.numberStyle = NSNumberFormatterSpellOutStyle;
        NSNumber *num = [fmt numberFromString:[numStr lowercaseString]];

        return num;
    

    return nil;


@end

然后我使用了一个简单的比较器块来处理数组:

NSArray *ingrediants = @[
    @"1/2 Cup Milk",
    @"125 grams Cashew",
    @"2 green onions",
    @"1/4 Cup Sugar",
    @"1/6 Spoon Salt",
    @"3/2 XYZ",
    @"One cup water",
    @"Almond oil"
];

NSArray *sorted = [ingrediants sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull str1, NSString * _Nonnull str2) 
    NSNumber *num1 = [str1 leadingNumber];
    NSNumber *num2 = [str2 leadingNumber];

    if (num1) 
        if (num2) 
            return [num1 compare:num2];
         else 
            return NSOrderedAscending;
        
     else 
        if (num2) 
            return NSOrderedDescending;
         else 
            return [str1 compare:str2 options:NSCaseInsensitiveSearch];
        
    
];

NSLog(@"Ordered: %@", sorted);

输出:

下单:( “1/6勺盐”, “1/4杯糖”, “1/2杯牛奶”, “一杯水”, "3/2 XYZ", “2个大葱”, “125克腰果”, “杏仁油” )

在处理拼写数字时,我的代码遇到了类似的问题。我的代码按原样只处理一个单词的数字。如果需要,处理多字拼写数字不会花费太多。

我的代码还要求任何分数中都不能有空格。同样,做一些工作可以解决这个限制。

【讨论】:

很高兴为您提供帮助。不要忘记,除了接受一个答案之外,您还可以对任意数量的答案进行投票。

以上是关于如何对 NSString 中的成分进行排序?前任。 1/2杯牛奶,1/4勺粉的主要内容,如果未能解决你的问题,请参考以下文章

如何按NSString长度对NSMutableArray进行排序?

对 NSMutableDictionary 进行排序

根据值列表对 NSArray 进行排序? [复制]

根据前任排序 SQL 结果

按降序排序数组(NSArray)

基于时间序列关系对休县的《前任3》歌词进行耦合程度研究