如何对 NSString 中的成分进行排序?前任。 1/2 杯牛奶,1/4 勺粉
How to sort ingredients inside NSString? Ex. 1/2 Cup Milk, 1/4 Spoon Powder
我在 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"
我知道这是可能的,但不知何故我无法弄清楚。
有谁做过类似的可以指导一下。
提前致谢。
您可以在 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
逻辑不可靠(它识别 "Thirty-Two",但不识别 "Thirty Two"),但如果您可以使用它想。或者你可以完全拉动它。
看起来 Rob post 在我忙着写自己的答案时写了他的好答案。既然我花了时间,我也可以 post 我的。它们相似又不同。
我也从 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);
输出:
Ordered: (
"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"
)
我的代码在处理拼写数字时遇到了类似的问题。我的代码按原样只处理单字数字。如果需要,处理多字拼写数字不会花费太多时间。
我的代码还要求任何分数都不能有空格。同样,稍加努力就可以解决此限制。
我在 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"
我知道这是可能的,但不知何故我无法弄清楚。
有谁做过类似的可以指导一下。
提前致谢。
您可以在 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
逻辑不可靠(它识别 "Thirty-Two",但不识别 "Thirty Two"),但如果您可以使用它想。或者你可以完全拉动它。
看起来 Rob post 在我忙着写自己的答案时写了他的好答案。既然我花了时间,我也可以 post 我的。它们相似又不同。
我也从 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);
输出:
Ordered: ( "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" )
我的代码在处理拼写数字时遇到了类似的问题。我的代码按原样只处理单字数字。如果需要,处理多字拼写数字不会花费太多时间。
我的代码还要求任何分数都不能有空格。同样,稍加努力就可以解决此限制。