Skip to content

Instantly share code, notes, and snippets.

@MojtabaHs
Last active January 24, 2024 08:13
Show Gist options
  • Save MojtabaHs/c9168775b54f3a6cc478ed87a9104bd4 to your computer and use it in GitHub Desktop.
Save MojtabaHs/c9168775b54f3a6cc478ed87a9104bd4 to your computer and use it in GitHub Desktop.
Converts between number and its abbreviation (1000 -> 1K -> 1000)
import Foundation
@dynamicMemberLookup
class NumberAbbreviationFormatter {
// MARK: - Internal properties
var formatter = NumberFormatter()
// MARK: - Static configurations
private let units = "KMBTPE"
.enumerated()
.map { (symbol: $0.element, value: pow(10, ($0.offset + 1) * 3)) }
// MARK: - Number to String conversion
func format(_ value: Int) -> String {
guard let unit = units.last(where: { $0.value.isLessThanOrEqualTo(Decimal(abs(value))) }) else {
return formatter.string(from: NSNumber(value: value)) ?? value.description
}
let factor = Decimal(value) / unit.value
let number = NSDecimalNumber(decimal: factor) as NSNumber
let formattedNumber = formatter.string(from: number) ?? number.description
return formattedNumber + String(unit.symbol).trimmingCharacters(in: .whitespaces)
}
// MARK: - String to Number conversion
func number(from string: String) -> Int? {
guard !string.isEmpty else { return nil }
guard string.last?.isNumber == false else { return formatter.number(from: string)?.intValue }
var string = string
let symbol = string.removeLast()
guard units.map(\.symbol).contains(symbol) else { return nil }
guard let number = formatter.number(from: string.trimmingCharacters(in: .whitespaces)) else { return nil }
let multiplier = units.first(where: { $0.symbol == symbol })?.value ?? 1
let result = number.decimalValue * multiplier
return NSDecimalNumber(decimal: result).intValue
}
}
// MARK: - Dynamic member lookup conformance
/// Access to the internal formatter
extension NumberAbbreviationFormatter {
subscript<T>(dynamicMember keyPath: KeyPath<NumberFormatter, T>) -> T {
formatter[keyPath: keyPath]
}
subscript<T>(dynamicMember keyPath: WritableKeyPath<NumberFormatter, T>) -> T {
get { formatter[keyPath: keyPath] }
set { formatter[keyPath: keyPath] = newValue }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment