Это похоже на недосмотр со стороны 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
означает на других языках. Высоко ценим всеобщее вдохновение.
person
TruMan1
schedule
30.05.2021