如何将所有超过 3 位的数字转换为 4 位或更少的数字?
这正是我的意思:
10345 = 10.3k
10012 = 10k
123546 = 123.5k
4384324 = 4.3m
四舍五入并不完全重要,而是一个额外的好处。
我已经研究过 NSNumberFormatter 但没有找到合适的解决方案,而且我还没有在 SO 上找到合适的解决方案。非常感谢任何帮助,谢谢!
如何将所有超过 3 位的数字转换为 4 位或更少的数字?
这正是我的意思:
10345 = 10.3k
10012 = 10k
123546 = 123.5k
4384324 = 4.3m
四舍五入并不完全重要,而是一个额外的好处。
我已经研究过 NSNumberFormatter 但没有找到合适的解决方案,而且我还没有在 SO 上找到合适的解决方案。非常感谢任何帮助,谢谢!
-(NSString*) suffixNumber:(NSNumber*)number
{
if (!number)
return @"";
long long num = [number longLongValue];
int s = ( (num < 0) ? -1 : (num > 0) ? 1 : 0 );
NSString* sign = (s == -1 ? @"-" : @"" );
num = llabs(num);
if (num < 1000)
return [NSString stringWithFormat:@"%@%lld",sign,num];
int exp = (int) (log10l(num) / 3.f); //log10l(1000));
NSArray* units = @[@"K",@"M",@"G",@"T",@"P",@"E"];
return [NSString stringWithFormat:@"%@%.1f%@",sign, (num / pow(1000, exp)), [units objectAtIndex:(exp-1)]];
}
样品使用
NSLog(@"%@",[self suffixNumber:@100]); // 100
NSLog(@"%@",[self suffixNumber:@1000]); // 1.0K
NSLog(@"%@",[self suffixNumber:@1500]); // 1.5K
NSLog(@"%@",[self suffixNumber:@24000]); // 24.0K
NSLog(@"%@",[self suffixNumber:@99900]); // 99.9K
NSLog(@"%@",[self suffixNumber:@99999]); // 100.0K
NSLog(@"%@",[self suffixNumber:@109999]); // 110.0K
NSLog(@"%@",[self suffixNumber:@5109999]); // 5.1M
NSLog(@"%@",[self suffixNumber:@8465445223]); // 8.5G
NSLog(@"%@",[self suffixNumber:[NSNumber numberWithInt:-120]]); // -120
NSLog(@"%@",[self suffixNumber:[NSNumber numberWithLong:-5000000]]); // -5.0M
NSLog(@"%@",[self suffixNumber:[NSNumber numberWithDouble:-3.5f]]); // -3
NSLog(@"%@",[self suffixNumber:[NSNumber numberWithDouble:-4000.63f]]); // -4.0K
[更新]
斯威夫特版本如下:
func suffixNumber(number:NSNumber) -> NSString {
var num:Double = number.doubleValue;
let sign = ((num < 0) ? "-" : "" );
num = fabs(num);
if (num < 1000.0){
return "\(sign)\(num)";
}
let exp:Int = Int(log10(num) / 3.0 ); //log10(1000));
let units:[String] = ["K","M","G","T","P","E"];
let roundedNum:Double = round(10 * num / pow(1000.0,Double(exp))) / 10;
return "\(sign)\(roundedNum)\(units[exp-1])";
}
样品使用
print(self.suffixNumber(NSNumber(long: 100))); // 100.0
print(self.suffixNumber(NSNumber(long: 1000))); // 1.0K
print(self.suffixNumber(NSNumber(long: 1500))); // 1.5K
print(self.suffixNumber(NSNumber(long: 24000))); // 24.0K
print(self.suffixNumber(NSNumber(longLong: 99900))); // 99.9K
print(self.suffixNumber(NSNumber(longLong: 99999))); // 100.0K
print(self.suffixNumber(NSNumber(longLong: 109999))); // 110.0K
print(self.suffixNumber(NSNumber(longLong: 5109999))); // 5.1K
print(self.suffixNumber(NSNumber(longLong: 8465445223))); // 8.5G
print(self.suffixNumber(NSNumber(long: -120))); // -120.0
print(self.suffixNumber(NSNumber(longLong: -5000000))); // -5.0M
print(self.suffixNumber(NSNumber(float: -3.5))); // -3.5
print(self.suffixNumber(NSNumber(float: -4000.63))); // -4.0K
希望能帮助到你
这是我的版本!感谢之前的回答。这个版本的目标是:
NSNumberFormatter
以避免位置问题(例如法语中的逗号而不是点)NSNumberFormatterRoundingMode
您可以使用所有精彩的NSNumberFormatter
选项来满足您的需求,请参阅NSNumberFormatter 类参考
代码(要点):
extension Int {
func formatUsingAbbrevation () -> String {
let numFormatter = NSNumberFormatter()
typealias Abbrevation = (threshold:Double, divisor:Double, suffix:String)
let abbreviations:[Abbrevation] = [(0, 1, ""),
(1000.0, 1000.0, "K"),
(100_000.0, 1_000_000.0, "M"),
(100_000_000.0, 1_000_000_000.0, "B")]
// you can add more !
let startValue = Double (abs(self))
let abbreviation:Abbrevation = {
var prevAbbreviation = abbreviations[0]
for tmpAbbreviation in abbreviations {
if (startValue < tmpAbbreviation.threshold) {
break
}
prevAbbreviation = tmpAbbreviation
}
return prevAbbreviation
} ()
let value = Double(self) / abbreviation.divisor
numFormatter.positiveSuffix = abbreviation.suffix
numFormatter.negativeSuffix = abbreviation.suffix
numFormatter.allowsFloats = true
numFormatter.minimumIntegerDigits = 1
numFormatter.minimumFractionDigits = 0
numFormatter.maximumFractionDigits = 1
return numFormatter.stringFromNumber(NSNumber (double:value))!
}
}
let testValue:[Int] = [598, -999, 1000, -1284, 9940, 9980, 39900, 99880, 399880, 999898, 999999, 1456384, 12383474]
testValue.forEach() {
print ("Value : \($0) -> \($0.formatUsingAbbrevation ())")
}
结果 :
Value : 598 -> 598
Value : -999 -> -999
Value : 1000 -> 1K
Value : -1284 -> -1.3K
Value : 9940 -> 9.9K
Value : 9980 -> 10K
Value : 39900 -> 39.9K
Value : 99880 -> 99.9K
Value : 399880 -> 0.4M
Value : 999898 -> 1M
Value : 999999 -> 1M
Value : 1456384 -> 1.5M
Value : 12383474 -> 12.4M
我遇到了同样的问题并最终使用了凯尔的方法,但不幸的是,当使用像120000这样的数字时它会中断,显示12k而不是120K,我需要显示小数字,例如:1.1K 而不是四舍五入到 1K。
所以这是我对凯尔最初想法的编辑:
Results:
[self abbreviateNumber:987] ---> 987
[self abbreviateNumber:1200] ---> 1.2K
[self abbreviateNumber:12000] ----> 12K
[self abbreviateNumber:120000] ----> 120K
[self abbreviateNumber:1200000] ---> 1.2M
[self abbreviateNumber:1340] ---> 1.3K
[self abbreviateNumber:132456] ----> 132.5K
-(NSString *)abbreviateNumber:(int)num {
NSString *abbrevNum;
float number = (float)num;
//Prevent numbers smaller than 1000 to return NULL
if (num >= 1000) {
NSArray *abbrev = @[@"K", @"M", @"B"];
for (int i = abbrev.count - 1; i >= 0; i--) {
// Convert array index to "1000", "1000000", etc
int size = pow(10,(i+1)*3);
if(size <= number) {
// Removed the round and dec to make sure small numbers are included like: 1.1K instead of 1K
number = number/size;
NSString *numberString = [self floatToString:number];
// Add the letter for the abbreviation
abbrevNum = [NSString stringWithFormat:@"%@%@", numberString, [abbrev objectAtIndex:i]];
}
}
} else {
// Numbers like: 999 returns 999 instead of NULL
abbrevNum = [NSString stringWithFormat:@"%d", (int)number];
}
return abbrevNum;
}
- (NSString *) floatToString:(float) val {
NSString *ret = [NSString stringWithFormat:@"%.1f", val];
unichar c = [ret characterAtIndex:[ret length] - 1];
while (c == 48) { // 0
ret = [ret substringToIndex:[ret length] - 1];
c = [ret characterAtIndex:[ret length] - 1];
//After finding the "." we know that everything left is the decimal number, so get a substring excluding the "."
if(c == 46) { // .
ret = [ret substringToIndex:[ret length] - 1];
}
}
return ret;
}
我希望这可以帮助你们。
Flávio J Vieira Caetano 的答案转换为 Swift 3.0
extension Int {
var abbreviated: String {
let abbrev = "KMBTPE"
return abbrev.characters.enumerated().reversed().reduce(nil as String?) { accum, tuple in
let factor = Double(self) / pow(10, Double(tuple.0 + 1) * 3)
let format = (factor.truncatingRemainder(dividingBy: 1) == 0 ? "%.0f%@" : "%.1f%@")
return accum ?? (factor > 1 ? String(format: format, factor, String(tuple.1)) : nil)
} ?? String(self)
}
}
我在尝试格式化 Shinobi Charts 中的 y 轴值时遇到了类似的问题。它需要使用 NSNumberFormatter,所以我最终想出了这个
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
[numFormatter setPositiveFormat:@"0M"];
[numFormatter setMultiplier:[NSNumber numberWithDouble:0.000001]];
获取格式化值
NSString *formattedNumber = [numFormatter stringFromNumber:[NSNumber numberWithInteger:4000000]]; //@"4M"
此解决方案不包含舍入,但如果您(或其他任何人)只需要一些简单的东西,这可以工作。如果您需要千而不是百万,则在 setPostiveFormat 方法中将“M”更改为“K”,并将乘数中的 NSNumber 值更改为 0.001 。
这是我想出的两种方法,它们可以共同产生预期的效果。这也将自动四舍五入。这还将通过传递 int dec 指定总共有多少数字可见。
此外,在 float to string 方法中,您可以将@"%.1f"
to @"%.2f"
、@"%.3f"
等更改为告诉它在小数点后显示多少个可见小数。
For Example:
52935 ---> 53K
52724 ---> 53.7K
-(NSString *)abbreviateNumber:(int)num withDecimal:(int)dec {
NSString *abbrevNum;
float number = (float)num;
NSArray *abbrev = @[@"K", @"M", @"B"];
for (int i = abbrev.count - 1; i >= 0; i--) {
// Convert array index to "1000", "1000000", etc
int size = pow(10,(i+1)*3);
if(size <= number) {
// Here, we multiply by decPlaces, round, and then divide by decPlaces.
// This gives us nice rounding to a particular decimal place.
number = round(number*dec/size)/dec;
NSString *numberString = [self floatToString:number];
// Add the letter for the abbreviation
abbrevNum = [NSString stringWithFormat:@"%@%@", numberString, [abbrev objectAtIndex:i]];
NSLog(@"%@", abbrevNum);
}
}
return abbrevNum;
}
- (NSString *) floatToString:(float) val {
NSString *ret = [NSString stringWithFormat:@"%.1f", val];
unichar c = [ret characterAtIndex:[ret length] - 1];
while (c == 48 || c == 46) { // 0 or .
ret = [ret substringToIndex:[ret length] - 1];
c = [ret characterAtIndex:[ret length] - 1];
}
return ret;
}
希望这可以帮助其他需要它的人!
在尝试了其中的几个解决方案之后,Luca laco 似乎最接近,但我对他的方法进行了一些修改,以便更好地控制将出现多少位(即,如果你希望 120.3K 更短,你可以将其限制为 120K)。此外,我添加了一个额外的步骤,以确保像 999,999 这样的数字不会显示为 1000.0K,而是 1.0M。
/*
With "onlyShowDecimalPlaceForNumbersUnder" = 10:
Original number: 598 - Result: 598
Original number: 1000 - Result: 1.0K
Original number: 1284 - Result: 1.3K
Original number: 9980 - Result: 10K
Original number: 39900 - Result: 40K
Original number: 99880 - Result: 100K
Original number: 999898 - Result: 1.0M
Original number: 999999 - Result: 1.0M
Original number: 1456384 - Result: 1.5M
Original number: 12383474 - Result: 12M
*/
- (NSString *)suffixNumber:(NSNumber *)number
{
if (!number)
return @"";
long long num = [number longLongValue];
if (num < 1000)
return [NSString stringWithFormat:@"%lld",num];
int exp = (int) (log(num) / log(1000));
NSArray * units = @[@"K",@"M",@"G",@"T",@"P",@"E"];
int onlyShowDecimalPlaceForNumbersUnder = 10; // Either 10, 100, or 1000 (i.e. 10 means 12.2K would change to 12K, 100 means 120.3K would change to 120K, 1000 means 120.3K stays as is)
NSString *roundedNumStr = [NSString stringWithFormat:@"%.1f", (num / pow(1000, exp))];
int roundedNum = [roundedNumStr integerValue];
if (roundedNum >= onlyShowDecimalPlaceForNumbersUnder) {
roundedNumStr = [NSString stringWithFormat:@"%.0f", (num / pow(1000, exp))];
roundedNum = [roundedNumStr integerValue];
}
if (roundedNum >= 1000) { // This fixes a number like 999,999 from displaying as 1000K by changing it to 1.0M
exp++;
roundedNumStr = [NSString stringWithFormat:@"%.1f", (num / pow(1000, exp))];
}
NSString *result = [NSString stringWithFormat:@"%@%@", roundedNumStr, [units objectAtIndex:(exp-1)]];
NSLog(@"Original number: %@ - Result: %@", number, result);
return result;
}
我知道已经有很多答案和不同的方法,但这就是我用更实用的方法解决它的方法:
extension Int {
var abbreviated: String {
let abbrev = "KMBTPE"
return abbrev.characters
.enumerated()
.reversed()
.reduce(nil as String?) { accum, tuple in
let factor = Double(self) / pow(10, Double(tuple.0 + 1) * 3)
let format = (factor - floor(factor) == 0 ? "%.0f%@" : "%.1f%@")
return accum ?? (factor >= 1 ? String(format: format, factor, String(tuple.1)) : nil)
} ?? String(self)
}
}
Swift-4 Doble extension
- 这在所有情况下都可以正常工作。
extension Double {
// Formatting double value to k and M
// 1000 = 1k
// 1100 = 1.1k
// 15000 = 15k
// 115000 = 115k
// 1000000 = 1m
func formatPoints() -> String{
let thousandNum = self/1000
let millionNum = self/1000000
if self >= 1000 && self < 1000000{
if(floor(thousandNum) == thousandNum){
return ("\(Int(thousandNum))k").replacingOccurrences(of: ".0", with: "")
}
return("\(thousandNum.roundTo(places: 1))k").replacingOccurrences(of: ".0", with: "")
}
if self > 1000000{
if(floor(millionNum) == millionNum){
return("\(Int(thousandNum))k").replacingOccurrences(of: ".0", with: "")
}
return ("\(millionNum.roundTo(places: 1))M").replacingOccurrences(of: ".0", with: "")
}
else{
if(floor(self) == self){
return ("\(Int(self))")
}
return ("\(self)")
}
}
/// Returns rounded value for passed places
///
/// - parameter places: Pass number of digit for rounded value off after decimal
///
/// - returns: Returns rounded value with passed places
func roundTo(places:Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
斯威夫特版本
从 Objective-C 版本直接翻译
func abbreviateNumber(num: NSNumber) -> NSString {
var ret: NSString = ""
let abbrve: [String] = ["K", "M", "B"]
var floatNum = num.floatValue
if floatNum > 1000 {
for i in 0..<abbrve.count {
let size = pow(10.0, (Float(i) + 1.0) * 3.0)
println("\(size) \(floatNum)")
if (size <= floatNum) {
let num = floatNum / size
let str = floatToString(num)
ret = NSString(format: "%@%@", str, abbrve[i])
}
}
} else {
ret = NSString(format: "%d", Int(floatNum))
}
return ret
}
func floatToString(val: Float) -> NSString {
var ret = NSString(format: "%.1f", val)
var c = ret.characterAtIndex(ret.length - 1)
while c == 48 {
ret = ret.substringToIndex(ret.length - 1)
c = ret.characterAtIndex(ret.length - 1)
if (c == 46) {
ret = ret.substringToIndex(ret.length - 1)
}
}
return ret
}
abbreviateNumber(123)
abbreviateNumber(12503)
abbreviateNumber(12934203)
abbreviateNumber(12234200003)
abbreviateNumber(92234203)
abbreviateNumber(9223.3)
你可以使用这个简单的功能,想法很容易理解
-(NSString*) suffixNumber:(NSNumber*)number
double value = [number doubleValue];
NSUInteger index = 0;
NSArray *suffixArray = @[@"", @"K", @"M", @"B", @"T", @"P", @"E"];
while ((value/1000) >= 1){
value = value/1000;
index++;
}
//3 line of code below for round doubles to 1 digit
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
[fmt setMaximumFractionDigits:1];
NSString *valueWith1Digit = [fmt stringFromNumber:[NSNumber numberWithFloat:value]];
NSString *svalue = [NSString stringWithFormat:@"%@%@",valueWith1Digit, [suffixArray objectAtIndex:index]];
return svalue;
}
测试
NSLog(@"%@",[self suffixNumber:@100]); // 100
NSLog(@"%@",[self suffixNumber:@1000]); // 1K
NSLog(@"%@",[self suffixNumber:@10345]); // 10.3K
NSLog(@"%@",[self suffixNumber:@10012]); // 10K
NSLog(@"%@",[self suffixNumber:@123456]); // 123.5K
NSLog(@"%@",[self suffixNumber:@4384324]); // 4.4M
NSLog(@"%@",[self suffixNumber:@10000000]) // 10M
来自 Phan Van Linh 回答的 Swift 4.0 版本
private static let suffix = ["", "K", "M", "B", "T", "P", "E"]
public static func formatNumber(_ number: Double) -> String{
var index = 0
var value = number
while((value / 1000) >= 1){
value = value / 1000
index += 1
}
return String(format: "%.1f%@", value, suffix[index])
}
更新了快速转换的答案
extension Int {
func abbreviateNumber() -> String {
func floatToString(val: Float) -> String {
var ret: NSString = NSString(format: "%.1f", val)
let c = ret.characterAtIndex(ret.length - 1)
if c == 46 {
ret = ret.substringToIndex(ret.length - 1)
}
return ret as String
}
var abbrevNum = ""
var num: Float = Float(self)
if num >= 1000 {
var abbrev = ["K","M","B"]
for var i = abbrev.count-1; i >= 0; i-- {
let sizeInt = pow(Double(10), Double((i+1)*3))
let size = Float(sizeInt)
if size <= num {
num = num/size
var numStr: String = floatToString(num)
if numStr.hasSuffix(".0") {
let startIndex = numStr.startIndex.advancedBy(0)
let endIndex = numStr.endIndex.advancedBy(-2)
let range = startIndex..<endIndex
numStr = numStr.substringWithRange( range )
}
let suffix = abbrev[i]
abbrevNum = numStr+suffix
}
}
} else {
abbrevNum = "\(num)"
let startIndex = abbrevNum.startIndex.advancedBy(0)
let endIndex = abbrevNum.endIndex.advancedBy(-2)
let range = startIndex..<endIndex
abbrevNum = abbrevNum.substringWithRange( range )
}
return abbrevNum
}
}
这是适用于 Swift 4的Luca Iaco答案的更新版本
func suffixNumber(number: NSNumber) -> String {
var num:Double = number.doubleValue
let sign = ((num < 0) ? "-" : "" )
num = fabs(num)
if (num < 1000.0) {
return "\(sign)\(num)"
}
let exp: Int = Int(log10(num) / 3.0)
let units: [String] = ["K","M","G","T","P","E"]
let roundedNum: Double = round(10 * num / pow(1000.0,Double(exp))) / 10
return "\(sign)\(roundedNum)\(units[exp-1])";
}
更清洁的解决方案:
struct Shortener {
func string(from value: String) -> String? {
guard let value = Int(value) else { return nil }
if value < 1000 {
return "\(value)"
}
if value < 100_000 {
return string(from: value, divisor: 1000, suffix: "K")
}
if value < 100_000_000 {
return string(from: value, divisor: 1_000_000, suffix: "M")
}
return string(from: value, divisor: 1_000_000_000, suffix: "B")
}
private func string(from value: Int, divisor: Double, suffix: String) -> String? {
let formatter = NumberFormatter()
let dividedValue = Double(value) / divisor
formatter.positiveSuffix = suffix
formatter.negativeSuffix = suffix
formatter.allowsFloats = true
formatter.minimumIntegerDigits = 1
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 1
return formatter.string(from: NSNumber(value: dividedValue))
}
}
gbitaudeau 在 Swift 4 中的回答
extension Int {
func formatUsingAbbrevation () -> String {
let numFormatter = NumberFormatter()
typealias Abbrevation = (threshold:Double, divisor:Double, suffix:String)
let abbreviations:[Abbrevation] = [(0, 1, ""),
(1000.0, 1000.0, "K"),
(100_000.0, 1_000_000.0, "M"),
(100_000_000.0, 1_000_000_000.0, "B")]
// you can add more !
let startValue = Double (abs(self))
let abbreviation:Abbrevation = {
var prevAbbreviation = abbreviations[0]
for tmpAbbreviation in abbreviations {
if (startValue < tmpAbbreviation.threshold) {
break
}
prevAbbreviation = tmpAbbreviation
}
return prevAbbreviation
} ()
let value = Double(self) / abbreviation.divisor
numFormatter.positiveSuffix = abbreviation.suffix
numFormatter.negativeSuffix = abbreviation.suffix
numFormatter.allowsFloats = true
numFormatter.minimumIntegerDigits = 1
numFormatter.minimumFractionDigits = 0
numFormatter.maximumFractionDigits = 1
return numFormatter.string(from: NSNumber (value:value))!
}
}
extension Int {
func abbreviateNumber() -> String {
func floatToString(val: Float) -> String {
var ret: NSString = NSString(format: "%.1f", val)
var c = ret.characterAtIndex(ret.length - 1)
if c == 46 {
ret = ret.substringToIndex(ret.length - 1)
}
return ret as String
}
var abbrevNum = ""
var num: Float = Float(self)
if num >= 1000 {
var abbrev = ["K","M","B"]
for var i = abbrev.count-1; i >= 0; i-- {
var sizeInt = pow(Double(10), Double((i+1)*3))
var size = Float(sizeInt)
if size <= num {
num = num/size
var numStr: String = floatToString(num)
if numStr.hasSuffix(".0") {
numStr = numStr.substringToIndex(advance(numStr.startIndex,count(numStr)-2))
}
var suffix = abbrev[i]
abbrevNum = numStr+suffix
}
}
} else {
abbrevNum = "\(num)"
if abbrevNum.hasSuffix(".0") {
abbrevNum = abbrevNum.substringToIndex(advance(abbrevNum.startIndex, count(abbrevNum)-2))
}
}
return abbrevNum
}
}
如果您对格式化字节数感兴趣,Mattt Thompson的这篇文章展示了如何使用 iOS/OSX 内置的 NSByteCountFormatter
关键在于,对于大多数常见的单元,您不需要编写任何自定义代码,因为 Apple 已经为您提供了繁琐的工作。检查他们的 NS[SomeUnit]Formatter 在线参考,例如MKDistanceFormatter
,NSDateIntervalFormatter
或NSDateFormatter
,等等...
我使用 gbitaudeau 的答案制作了这个 Objective-C 类别的 NSNumberFormatter,我在我们的项目 ( Vero.co ) 中使用了它。这里的 NSNumberFormatter 实例只为整个项目创建了一次。
@implementation NSNumberFormatter (Abbreviation)
+ (NSString*) abbreviatedStringFromNumber:(NSNumber*) number
{
static dispatch_once_t pred;
static NSNumberFormatter* __abbrFormatter = nil;
static NSArray<NSDictionary*> * __abbreviations = nil;
dispatch_once(&pred, ^{
__abbrFormatter = [[NSNumberFormatter alloc] init];
__abbrFormatter.numberStyle = NSNumberFormatterDecimalStyle;
__abbrFormatter.usesGroupingSeparator = YES;
__abbrFormatter.allowsFloats = YES;
__abbrFormatter.minimumIntegerDigits = 1;
__abbrFormatter.minimumFractionDigits = 0;
__abbrFormatter.maximumFractionDigits = 2;
__abbreviations = @[@{@"threshold":@(0.0), @"divisor":@(1.0), @"suffix":@""},
@{@"threshold":@(1000.0), @"divisor":@(1000.0), @"suffix":@"K"},
@{@"threshold":@(1000000.0), @"divisor":@(1000000.0), @"suffix":@"M"}];
});
double startValue = ABS([number doubleValue]);
NSDictionary* abbreviation = __abbreviations[0];
for (NSDictionary* tmpAbbr in __abbreviations)
{
if (startValue < [tmpAbbr[@"threshold"] doubleValue])
{
break;
}
abbreviation = tmpAbbr;
}
double value = [number doubleValue] / [abbreviation[@"divisor"] doubleValue];
[__abbrFormatter setLocale:[NSLocale currentLocale]]; //user might change locale while the app is sleeping
[__abbrFormatter setPositiveSuffix:abbreviation[@"suffix"]];
[__abbrFormatter setNegativeSuffix:abbreviation[@"suffix"]];
return [__abbrFormatter stringFromNumber:@(value)];
}
@end
你现在可以这样称呼它
[NSNumberFormatter abbreviatedStringFromNumber:@(N)];
swift 4 和 swift 5 兼容的解决方案
extension Int {
func formatUsingAbbrevation () -> String {
let abbrev = "KMBTPE"
return abbrev.enumerated().reversed().reduce(nil as String?) { accum, tuple in
let factor = Double(self) / pow(10, Double(tuple.0 + 1) * 3)
let format = (factor.truncatingRemainder(dividingBy: 1) == 0 ? "%.0f%@" : "%.1f%@")
return accum ?? (factor > 1 ? String(format: format, factor, String(tuple.1)) : nil)
} ?? String(self)
}
}
与此处的大多数解决方案不同,以下方法同时处理正数和负数。
它甚至也适用于货币。
BOOL isCurrency = YES; // Make it YES / NO depending upon weather your input value belongs to a revenue figure or a normal value.
double value = XXX ; // where 'XXX' is your input value
NSString *formattedValue = @"";
int decimalPlaces = 1; // number of decimal places (precision) that you want.
float multiplier;
// Enumerate number abbreviations
NSArray *abbrevations = @[@"", @"k", @"m", @"b", @"t" ];
// Go through the array backwards, so we do the largest first
int index;
for (index = abbrevations.count-1; index >= 0; index--) {
multiplier = pow(10, decimalPlaces);
// Convert array index to "1000", "1000000", etc
double size = pow(10, index*3);
// If the number is bigger or equal do the abbreviation
if(size <= fabs(round(value)))
{
// Here, we multiply by multiplier, round, and then divide by multiplier.
// This gives us nice rounding to a particular decimal place.
value = round(value * multiplier / size) / multiplier;
// We are done... stop
break;
}
}
if (index<0)
{
// Note: - To handle special case where x is our input number, -0.5 > x < 0.5
value = 0;
index++;
}
NSString *stringFormat = nil;
// Add the letter for the abbreviation
if (isCurrency)
{
if (value >=0)
{
stringFormat = [NSString stringWithFormat:@"$%%.%0df%@", decimalPlaces, abbrevations[index]];
}
else
{
// Note: - To take care of extra logic where '$' symbol comes after '-' symbol for negative currency.
stringFormat = [NSString stringWithFormat:@"-$%%.%df%@", decimalPlaces, abbrevations[index]];
value = -value;
}
}
else
{
stringFormat = [NSString stringWithFormat:@"%%.%0df%@", decimalPlaces, abbrevations[index]];
}
formattedValue = [NSString stringWithFormat:stringFormat, value];
输出如下
In Currency mode
'999' ---- '$999.0'
'999.9' ---- '$1.0k'
'999999.9' ---- '$1.0m'
'-1000.1' ---- '-$1.0k'
'-0.9' ---- '-$0.9'
In Number mode
'999' ---- '999.0'
'999.9' ---- '1.0k'
'1' ---- '1.0'
'9999' ---- '10.0k'
'99999.89999999999' ---- '100.0k'
'999999.9' ---- '1.0m'
'-1' ---- '-1.0'
'-1000.1' ---- '-1.0k'
'5109999' ---- '5.1m'
'-5109999' ---- '-5.1m'
'999999999.9' ---- '1.0b'
'0.1' ---- '0.0'
'0' ---- '0.0'
'-0.1' ---- '0.0'
'-0.9' ---- '-0.9'
我根据@Kyle Begeman 从@Pandiyan Cool 分享的链接的原始灵感创建了上述方法。感谢@Jeff B 提供来自以下链接的 Javascript 初始代码。有没有办法将数字四舍五入为读者友好的格式?(例如 1.1 万美元)
Swift 2.2 作为双扩展:
extension Double {
var suffixNumber : String {
get {
var num = self
let sign = ((num < 0) ? "-" : "" )
num = fabs(num)
if (num < 1000.0){
return "\(sign)\(num)"
}
let exp:Int = Int(log10(num) / 3.0 )
let units:[String] = ["K","M","G","T","P","E"]
let roundedNum = round(10 * num / pow(1000.0,Double(exp))) / 10
return "\(sign)\(roundedNum)\(units[exp-1])"
}
}
}
在 Swift 5 中为土耳其语或其他语言使用超过 1 个字符:
extension Int {
var abbreviated: String {
let trSuffix = "B,Mn,Mr,T,Kt,Kn"
let abbrev = trSuffix.split(separator: ",")
return abbrev.enumerated().reversed().reduce(nil as String?) { accum, tuple in
let factor = Double(self) / pow(10, Double(tuple.0 + 1) * 3)
let format = (factor.truncatingRemainder(dividingBy: 1) == 0 ? "%.0f%@" : "%.1f%@")
return accum ?? (factor > 1 ? String(format: format, factor, String(tuple.1)) : nil)
} ?? String(self)
}
这似乎是 Apple 的疏忽,因为有大量的相对时间、指标、日期、列表、人员、字节等格式化程序,但这是一个非常常见的情况,尤其是社交媒体、图表等。好的结束咆哮..
下面是我的版本,它包含NumberFormatter
并处理所有Int
值,包括否定值以及语言环境感知:
public struct AbbreviatedNumberFormatter {
private let formatter: NumberFormatter
public init(locale: Locale? = nil) {
let formatter = NumberFormatter()
formatter.allowsFloats = true
formatter.minimumIntegerDigits = 1
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 1
formatter.numberStyle = .decimal
if let locale = locale {
formatter.locale = locale
}
self.formatter = formatter
}
}
public extension AbbreviatedNumberFormatter {
/// Returns a string containing the formatted value of the provided `Int` value.
func string(from value: Int) -> String {
let divisor: Double
let suffix: String
switch abs(value) {
case ..<1000:
return "\(value)"
case ..<1_000_000:
divisor = 1000
suffix = "K"
case ..<1_000_000_000:
divisor = 1_000_000
suffix = "M"
case ..<1_000_000_000_000:
divisor = 1_000_000_000
suffix = "B"
default:
divisor = 1_000_000_000_000
suffix = "T"
}
let number = NSNumber(value: Double(value) / divisor)
guard let formatted = formatter.string(from: number) else {
return "\(value)"
}
return formatted + suffix
}
}
和测试用例:
final class AbbreviatedNumberFormatterTests: XCTestCase {}
extension AbbreviatedNumberFormatterTests {
func testFormatted() {
let formatter = AbbreviatedNumberFormatter()
XCTAssertEqual(formatter.string(from: 0), "0")
XCTAssertEqual(formatter.string(from: -10), "-10")
XCTAssertEqual(formatter.string(from: 500), "500")
XCTAssertEqual(formatter.string(from: 999), "999")
XCTAssertEqual(formatter.string(from: 1000), "1K")
XCTAssertEqual(formatter.string(from: 1234), "1.2K")
XCTAssertEqual(formatter.string(from: 9000), "9K")
XCTAssertEqual(formatter.string(from: 10_000), "10K")
XCTAssertEqual(formatter.string(from: -10_000), "-10K")
XCTAssertEqual(formatter.string(from: 15_235), "15.2K")
XCTAssertEqual(formatter.string(from: -15_235), "-15.2K")
XCTAssertEqual(formatter.string(from: 99_500), "99.5K")
XCTAssertEqual(formatter.string(from: -99_500), "-99.5K")
XCTAssertEqual(formatter.string(from: 100_500), "100.5K")
XCTAssertEqual(formatter.string(from: -100_500), "-100.5K")
XCTAssertEqual(formatter.string(from: 105_000_000), "105M")
XCTAssertEqual(formatter.string(from: -105_000_000), "-105M")
XCTAssertEqual(formatter.string(from: 140_800_200_000), "140.8B")
XCTAssertEqual(formatter.string(from: 170_400_800_000_000), "170.4T")
XCTAssertEqual(formatter.string(from: -170_400_800_000_000), "-170.4T")
XCTAssertEqual(formatter.string(from: -9_223_372_036_854_775_807), "-9,223,372T")
XCTAssertEqual(formatter.string(from: Int.max), "9,223,372T")
}
}
extension AbbreviatedNumberFormatterTests {
func testFormattedLocale() {
let formatter = AbbreviatedNumberFormatter(locale: Locale(identifier: "fr"))
XCTAssertEqual(formatter.string(from: 0), "0")
XCTAssertEqual(formatter.string(from: -10), "-10")
XCTAssertEqual(formatter.string(from: 500), "500")
XCTAssertEqual(formatter.string(from: 999), "999")
XCTAssertEqual(formatter.string(from: 1000), "1K")
XCTAssertEqual(formatter.string(from: 1234), "1,2K")
XCTAssertEqual(formatter.string(from: 9000), "9K")
XCTAssertEqual(formatter.string(from: 10_000), "10K")
XCTAssertEqual(formatter.string(from: -10_000), "-10K")
XCTAssertEqual(formatter.string(from: 15_235), "15,2K")
XCTAssertEqual(formatter.string(from: -15_235), "-15,2K")
XCTAssertEqual(formatter.string(from: 99_500), "99,5K")
XCTAssertEqual(formatter.string(from: -99_500), "-99,5K")
XCTAssertEqual(formatter.string(from: 100_500), "100,5K")
XCTAssertEqual(formatter.string(from: -100_500), "-100,5K")
XCTAssertEqual(formatter.string(from: 105_000_000), "105M")
XCTAssertEqual(formatter.string(from: -105_000_000), "-105M")
XCTAssertEqual(formatter.string(from: 140_800_200_000), "140,8B")
XCTAssertEqual(formatter.string(from: -170_400_800_000_000), "-170,4T")
XCTAssertEqual(formatter.string(from: -9_223_372_036_854_775_807), "-9 223 372T")
XCTAssertEqual(formatter.string(from: Int.max), "9 223 372T")
}
}
我唯一不喜欢它的是它没有本地化到什么K
, M
, B
, 或T
其他语言的含义。非常感谢大家的启发。
为什么你们都这么难?
它可以像这样简单:
-(NSString *)friendlyNumber:(long long)num{
NSString *stringNumber;
if (num < 1000) {
stringNumber = [NSString stringWithFormat:@"%lld", num];
}else if(num < 1000000){
float newNumber = floor(num / 100) / 10.0;
stringNumber = [NSString stringWithFormat:@"%.1fK", newNumber];
}else{
float newNumber = floor(num / 100000) / 10.0;
stringNumber = [NSString stringWithFormat:@"%.1fM", newNumber];
}
return stringNumber;
}