Skip to content

Instantly share code, notes, and snippets.

@erica
Created December 14, 2018 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erica/26caaa0984ad8ffe7a98cb6d71596363 to your computer and use it in GitHub Desktop.
Save erica/26caaa0984ad8ffe7a98cb6d71596363 to your computer and use it in GitHub Desktop.
// More String Interpolation
public extension String {
/// The direction from which a string floats when
/// padded with repeated characters.
enum PaddingDirection { case left, right, center }
/// Returns a new string repeatedly padded with a character on
/// either or both sides of the source string.
///
/// - Returns: A padded string
func padded(
_ direction: PaddingDirection,
with character: Character,
toWidth targetWidth: Int = 0
) -> String {
var width = targetWidth
// Default the padding to one pad on each side
if targetWidth == 0 {
switch direction {
case .left, .right: width = count + 1
case .center: width = count + 2
}
}
// Determine core padding required
guard width > count else { return self }
let corePadCount = width - count
func padding(_ count: Int) -> String {
return String(repeating: character, count: count)
}
switch direction {
case .left:
// Left pad "abc" to width 5 with " " -> " abc"
return padding(corePadCount) + self
case .right:
// Right pad "abc" to width 5 with " " -> "abc "
return self + padding(corePadCount)
case .center:
// Center pad "abc" to width 5 with " " -> " abc "
let halfPad = corePadCount / 2
return padding(corePadCount - halfPad) + self + padding(halfPad)
}
}
}
public extension String.StringInterpolation {
/// Return character-padded version of the value
mutating func appendInterpolation<T>(_ value: T, width: Int, direction: String.PaddingDirection, pad: Character = " ") {
appendLiteral("\(value)".padded(direction, with: pad, toWidth: width))
}
}
"\(24.5, width: 6, direction: .right, pad: "0")" // 24.500
"\("Hello", width: 10, direction: .center)" // " Hello "
public extension BinaryInteger {
/// Returns front-padded integer with leading zeroes
func stringFrontPadded(to count: Int, radix: Int = 10) -> String {
var string = String(self, radix: radix)
while string.count < count {
string = "0" + string
}
return string
}
}
public extension String.StringInterpolation {
/// Standard binary prefix
static let binaryPrefix = "0b"
/// Standard octal prefix
static let octalPrefix = "0o"
/// Standard hex prefix
static let hexPrefix = "0x"
/// Return padded version of the value
mutating func appendInterpolation<Number: BinaryInteger>(_ value: Number, toWidth width: Int = 0, radix: Int = 10, prefix: Bool = false) {
var prefixString = ""
if prefix {
switch radix {
case 2:
prefixString += String.StringInterpolation.binaryPrefix
case 8:
prefixString += String.StringInterpolation.octalPrefix
case 16:
prefixString += String.StringInterpolation.hexPrefix
default:
break
}
}
let valueString = value.stringFrontPadded(to: width, radix: radix)
appendInterpolation(prefixString + valueString)
}
}
"\(0x2A, toWidth:6, radix: 16, prefix: true)" // 0x00002a
"\(0x2A, toWidth:6, radix: 16)" // 00002a
"\(42, toWidth:6, radix: 16)" // 00002a
import QuartzCore
/// Numbers that can be represented as Double instances
public protocol DoubleRepresentable {
/// Returns self as a `Double` value
var doubleValue: Double { get }
}
/// Numbers that convert to other types
public protocol ConvertibleNumberType: DoubleRepresentable {
/// Returns self as a `Float` value
var floatValue: Float { get }
/// Returns self as an `Int` value
var intValue: Int { get }
/// Returns self as a `CGFloat` value
var cgFloatValue: CGFloat { get }
}
// Default conversion implementations
public extension ConvertibleNumberType {
/// Returns self as a `Float` value
var floatValue: Float { get { return Float(doubleValue) } }
/// Returns self as an `Int` value
var intValue: Int { get { return lrint(doubleValue) } }
/// Returns self as a `CGFloat` value
var cgFloatValue: CGFloat { get { return CGFloat(doubleValue) } }
}
// Conform to ConvertibleNumberType
extension Double: ConvertibleNumberType {
/// Returns self as a double value
public var doubleValue: Double { return self }
}
// Conform to ConvertibleNumberType
extension Int: ConvertibleNumberType {
/// Returns self as a double value
public var doubleValue: Double { return Double(self) }
}
// Conform to ConvertibleNumberType
extension CGFloat: ConvertibleNumberType {
/// Returns self as a double value
public var doubleValue: Double { return Double(self) }
}
// Conform to ConvertibleNumberType
extension Float: ConvertibleNumberType {
/// Returns self as a double value
public var doubleValue: Double { return Double(self) }
}
public extension ConvertibleNumberType {
/// Pad a number to a set number of digits after the decimal
/// point without rounding.
func toPrecision(digits: Int) -> String {
// Zero digits omits decimal point to return Int representation
guard digits != 0 else {
return "\(Int(floor(self.doubleValue)))"
}
// Convert to double and check for "."
var string = "\(self.doubleValue)"
guard let range = string.range(of: ".")
else { return string }
// Truncate
let distance = string.distance(from: range.lowerBound, to: string.endIndex)
if distance > digits {
return String(string.prefix(string.count - (distance - (digits + 1))))
}
// Pad
while string.distance(from: range.lowerBound, to: string.endIndex) <= digits {
string += "0"
}
return string
}
}
public extension String.StringInterpolation {
/// Return decimal-padded version of the value
mutating func appendInterpolation<Number: ConvertibleNumberType>(_ value: Number, places: Int) {
appendInterpolation(value.toPrecision(digits: places))
}
}
"\(50, places:0)" // 50
"\(50, places:1)" // 50.0
"\(Double.pi, places:1)" // 3.1
"\(Double.pi, places:2)" // 3.14
"\(Double.pi, places:6)" // 3.141592
"\(Double.pi, places:20)" // 3.14159265358979300000
"\(49.9837, places:0)" // 49
"\(49.9837, places:1)" // 49.9
"\(49.9837, places:2)" // 49.98
"\(49.9837, places:3)" // 49.983
"\(49.9837, places:4)" // 49.9837
"\(49.9837, places:5)" // 49.98370
"\(49.9837, places:8)" // 49.98370000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment