iOS将大数字转换为较小的格式

时间:2013-08-16 06:37:08

标签: ios formatting nsnumber data-conversion human-readable

如何将超过3位的所有数字转换为4位或更少的数字?

这正是我的意思:

10345 = 10.3k
10012 = 10k
123546 = 123.5k
4384324 = 4.3m
舍入并不是完全重要的,但还有一个额外的好处。

我已经研究过NSNumberFormatter但是没有找到合适的解决方案,而且我还没有在SO上找到合适的解决方案。非常感谢任何帮助,谢谢!

25 个答案:

答案 0 :(得分:56)

-(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

[<强>更新

下面的Swift版本:

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

希望有所帮助

答案 1 :(得分:35)

我遇到了同样的问题并且最终使用了Kyle的方法,但不幸的是,当使用 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;
}

我希望这可以帮助你们。

答案 2 :(得分:31)

这是我的版本!感谢以前的答案。 这个版本的目标是:

  • 有更好的阈值控制,因为少数细节对于非常大的数字细节更为重要
  • 尽可能使用NSNumberFormatter以避免位置问题(例如逗号代替法语中的点)
  • 避免&#34; .0&#34;和四舍五入的数字,可以使用NSNumberFormatterRoundingMode
  • 进行自定义

您可以使用所有精彩的NSNumberFormatter选项来满足您的需求,请参阅NSNumberFormatter Class Reference

代码(gist):

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

答案 3 :(得分:23)

Flávio J Vieira Caetano's answer转换为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)
    }
}

答案 4 :(得分:13)

我遇到了类似的问题,试图在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。

答案 5 :(得分:10)

以下是我提出的两种方法,它们共同发挥作用以产生预期的效果。这也将自动向上舍入。这也将通过传递int dec来指定可见的总数。

此外,在float to string方法中,您可以将@"%.1f"更改为@"%.2f"@"%.3f"等,以告知它在小数点后显示多少可见小数。< / p>

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;
}

希望这可以帮助其他需要它的人!

答案 6 :(得分:7)

在尝试了几个这样的解决方案之后,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;
}

答案 7 :(得分:5)

我知道已经有很多答案和不同的方法,但这就是我用更实用的方法解决它的方法:

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)
    }
}

答案 8 :(得分:4)

Swift版

直接从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)

答案 9 :(得分:3)

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
    }
}

enter image description here
enter image description here
enter image description here
enter image description here

答案 10 :(得分: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

答案 11 :(得分:2)

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))!
}

}

答案 12 :(得分:2)

来自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])
}

答案 13 :(得分:2)

以下是适用于Swift 4的Luca Iaco's答案的更新版本

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])";
}

答案 14 :(得分:2)

更新了快速转换的答案

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
    }
}

答案 15 :(得分:1)

我使用gbitaudeau的答案来制作我在项目中使用的NSNumberFormatter的Objective-C类别(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)];

答案 16 :(得分:1)

If you are interested in formatting bytes count, this article by Mattt Thompson shows how to use iOS/OSX builtin NSByteCountFormatter

There are also builtin formatters for energy, mass, length and a bunch of others.

The crux of it is that for most common units you do not need to write any custom code as Apple has already provided the tedious work for you. Check their online reference for NS[SomeUnit]Formatter, e.g. MKDistanceFormatter, NSDateIntervalFormatter or NSDateFormatter, etc ...

答案 17 :(得分:1)

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
    }
}

答案 18 :(得分:0)

更清洁的解决方案:

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))
    }

}

答案 19 :(得分:0)

这似乎是 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")
    }
}

我唯一不喜欢的是它没有本地化 KMBT 在其他语言中的含义。非常感谢大家的启发。

答案 20 :(得分:0)

在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)
    }

答案 21 :(得分:0)

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)
    }
}

答案 22 :(得分:0)

Swift 2.2作为Double扩展名:

ImageView iv = (ImageView) view;
iv.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        openContextMenu(v);
        String s = "Task Done";
        ====>>>Snackbar.make(findViewById(android.R.id.content), s, Snackbar.LENGTH_LONG).show();<====error shown in this line
    }
});

答案 23 :(得分:0)

与大多数解决方案不同,以下方法处理正数和负数。

它甚至适用于货币。

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中的初始代码。 Is there a way to round numbers into a reader friendly format? (e.g. $1.1k)

答案 24 :(得分:-1)

为什么你们这么难受?

它可以这么简单:

-(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;
}