So i need to make the decimal places smaller than the actual number i will show you what i mean below.
this is the code i have:
import SwiftUI
struct BalanceDetailsView: View {
//MARK: - PROPERTIES
var balance: Float
var balanceString: String {
return balance.formattedWithSeparator
}
//MARK: - BODY
var body: some View {
VStack{
HStack{
Text(balanceString)
.font(.custom(K.fonts.gilroyBold, size: 24))
.multilineTextAlignment(.center)
.padding(.top)
}//:VSTACK
}//:HSTACK
}
}
struct BalanceDetailsView_Previews: PreviewProvider {
static var previews: some View {
BalanceDetailsView(balance: 43678)
.previewLayout(.sizeThatFits)
}
}
//Formatter extension i used to get this code
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
// minimum decimal digit, eg: to display 2 as 2.00
formatter.minimumFractionDigits = 2
// maximum decimal digit, eg: to display 2.5021 as 2.50
formatter.maximumFractionDigits = 2
return formatter
}()
}
extension Numeric {
var formattedWithSeparator: String { Formatter.withSeparator.string(for: self) ?? "" }
}
Result I get
Result I need
When you know exact format of your string, like in this case minimum string length will be 4("0.00") , you can safely use dropLast and dropFirst.
I suggest moving 2 to priceFractionDigits constant to reduce constants usage in your code.
Then you can use string concatenation, it'll align Text by baseline.
struct BalanceText: View {
var balance: Float
var balanceString: String {
return balance.formattedWithSeparator
}
var body: some View {
Text(balanceString.dropLast(priceFractionDigits))
.font(.system(size: 24))
+
Text(balanceString.dropFirst(balanceString.count - priceFractionDigits))
.font(.system(size: 18))
}
}
private let priceFractionDigits = 2
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
// minimum decimal digit, eg: to display 2 as 2.00
formatter.minimumFractionDigits = priceFractionDigits
// maximum decimal digit, eg: to display 2.5021 as 2.50
formatter.maximumFractionDigits = priceFractionDigits
return formatter
}()
}
Usage
BalanceText(balance: balance)
Here is an example using the new AttributedString in SwiftUI 3 (iOS 15, macOS 12 etc)
var balanceString: AttributedString {
var attributedString = AttributedString(balance.formattedWithSeparator)
guard let separator = Formatter.withSeparator.decimalSeparator else { return attributedString }
if let range = attributedString.range(of: separator) {
attributedString[attributedString.startIndex...attributedString.index(beforeCharacter: range.lowerBound)]
.font = Font.largeTitle
attributedString[attributedString.index(afterCharacter: range.lowerBound)..<attributedString.endIndex]
.font = Font.caption
}
return attributedString
}
I used some built in font styles here but that should be easy to replace. Also note that since we set the .font attribute here it should be removed from Text
You can concatenate Text together with the + operator.
I slightly change the way you use NumberFormatter, so it's split by the correct decimal character for the locale.
struct BalanceDetailsView: View {
private let wholeNumberPart: String
private let decimalNumberPart: String
init(balance: Double) {
(wholeNumberPart, decimalNumberPart) = balance.formattedSplittingBySeparator
}
var body: some View {
Text(wholeNumberPart).font(.system(size: 24)) +
Text(decimalNumberPart).font(.system(size: 16))
}
}
extension Numeric {
var formattedSplittingBySeparator: (whole: String, decimal: String) {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
let str = formatter.string(for: self) ?? "0\(formatter.decimalSeparator!)00"
let split = str.components(separatedBy: formatter.decimalSeparator)
let whole = (split.first ?? "0") + formatter.decimalSeparator
let decimal = split.count == 2 ? split[1] : "00"
return (whole: whole, decimal: decimal)
}
}
Usage:
BalanceDetailsView(balance: 43678)
Result:
You could split the string by the decimal separator and display the parts in a zero-spacing HStack.
struct BalanceView : View {
let balance : Float
var body: some View {
let components = balance
.formattedWithSeparator
.components(separatedBy: Formatter.withSeparator.decimalSeparator)
HStack(alignment: .firstTextBaseline, spacing: 0) {
Text(components[0])
if components.count > 1 {
Text(".")
Text(components[1])
.font(.title)
}
}
.font(.largeTitle)
.multilineTextAlignment(.center)
.padding(.top)
}
}
Replace the fonts with your custom fonts
In your environment you could use it
struct BalanceDetailsView: View {
//MARK: - PROPERTIES
var balance: Float
//MARK: - BODY
var body: some View {
VStack{
BalanceView(balance: balance)
}//:VSTACK
}
}
Related
Overall Context
I begin with the below View, which gives me the result image of "Total 0.00" that follows.
My problem: When I enter in values, it either adds the numbers at the beginning or the end of the "0.00" placeholder String in TextField. I can highlight and overwrite the placeholder text of the TextField in the preview with keyboard input.
My goal: I want the input sequence to run as such:
User inputs the first digit of their purchase amount
This first digit replaces the 0 in the 2nd decimal place of the placeholder text in TextField.
As the user input the remaining digits of their purchaser price, the numbers move right to left.
Example: Let's say the user's purchase price was 29.50:
User inputs 2
The TextField changes from 0.00 to 0.02
User inputs 9, the TextField then reads 0.29
This continues until the user finishes inputing 29.50.
TLDR I want the input to run right to left, keeping the decimal in the appropriate place.
import SwiftUI
struct ContentView: View {
#State private var total: String = "0.00"
var body: some View {
NavigationView {
Form {
Section(header: Text("Detail")) {
HStack {
Text("Total")
TextField("0.00", text: $total)
.keyboardType(.decimalPad)
.foregroundColor(.gray)
.frame(width: 120)
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Result of the above ContentView
Attempt to use .onEditingChanged
I replaced the TextField with the below. Trying the below code returns the following error on line the line with .onEditingChanged "Value of type 'some View' has no member 'onEditingChanged'"
I learned .onEditingChanged is not a property of TextField so I needed to try another approach....
TextField("0.00", text: $total)
.keyboardType(.numberPad)
.foregroundColor(.gray)
.frame(width: 120)
.onEditingChanged { value in
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale(identifier: "en_US")
if let result = formatter.string(from: NSNumber(value: Double(value) ?? 0)) {
self.total = result
}
}
Attempt to use .onCommit {}
I replaced the .onEditingChanged { value in from that attempt with .onCommit {. This resulted in the same error message. It read "Value of type 'some View' has no member 'onCommit'"
TextField("0.00", text: $total)
.keyboardType(.decimalPad)
.foregroundColor(.gray)
.frame(width: 120)
.onCommit {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale(identifier: "en_US")
if let result = formatter.string(from: NSNumber(value: Double(self.total) ?? 0)) {
self.total = result
}
}
I am at a loss as how to achieve my goal. Thank you for the help in advance!
I'm having a bit of issue with my code...right now, I am passing a string containing a bunch of numbers, to get converted to a number, comma separators added, then converted back to a string for output. When I add a decimal to my string and pass it in, a number like 996.3658 get truncated to 996.366...
"currentNumber" is my input value, "textOutputToScreen" is my output...
func formatNumber() {
let charset = CharacterSet(charactersIn: ".")
if let _ = currentNumber.rangeOfCharacter(from: charset) {
if let number = Float(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
else {
if let number = Int(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
}
Thank you in advance for your help!
The issue there is that you have to set your NumberFormatter minimumFractionDigits to 4. Btw there is no need to initialize a NSNumber object. You can use Formatters string(for: Any) method and pass your Float. Btw I would use a Double (64-bit) instead of a Float (32-bit) and there is no need to initialize a new string g from your formattedNumber object. It is already a String.
Another thing is that you don't need to know the location of the period you can simply use contains instead of rangeOfCharacter method. Your code should look something like this:
extension Formatter {
static let number: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
}
func formatNumber(from string: String) -> String? {
if string.contains(".") {
guard let value = Double(string) else { return nil }
Formatter.number.minimumFractionDigits = 4
return Formatter.number.string(for: value)
} else {
guard let value = Int(string) else { return nil }
Formatter.number.minimumFractionDigits = 0
return Formatter.number.string(for: value)
}
}
let label = UILabel()
let currentNumber = "996.3658"
label.text = formatNumber(from: currentNumber) // "996.3658\n"
If you would like to assign the result to your var instead of a label
if let formatted = formatNumber(from: currentNumber) {
textOutputToScreen = formatted
}
I have been playing with MeasurementFormatter to try and display imperial lengths as 10'6" or 10 ft 6 in unsuccessfully. LengthFormatter does this correctly when isForPersonHeightUse is set to true, but it does not adapt to the user's locale well (i.e. countries where length is measured in metric, except for when referring to height). Is there any way to force this behaviour in a formatter?
EDIT
This question is to determine how to choose the units for a measurement. I am able to choose feet or inches, but want to display fractional feet as inches as in: 6'3" instead of 6.25 ft.
public struct LengthFormatters {
public static let imperialLengthFormatter: LengthFormatter = {
let formatter = LengthFormatter()
formatter.isForPersonHeightUse = true
return formatter
}()
}
extension Measurement where UnitType : UnitLength {
var heightOnFeetsAndInches: String? {
guard let measurement = self as? Measurement<UnitLength> else {
return nil
}
let meters = measurement.converted(to: .meters).value
LengthFormatters.imperialLengthFormatter.string(fromMeters: meters)
}
}
Example of using:
let a = Measurement(value: 6.34, unit: UnitLength.feet)
print(a.heightOnFeetsAndInches ?? "")
let b = Measurement(value: 1.5, unit: UnitLength.feet)
print(b.heightOnFeetsAndInches ?? "")
Will print:
6 ft, 4.08 in
1 ft, 6 in
I modified (simplified) #maslovsa's answer to meet my needs. I have a Core Data object called "Patient". It has a height parameter in inches that is an Int64. I want a string that I display to the user, so here's my property on my patient object for doing so:
var heightInFeetString : String {
let measurement = Measurement(value: Double(self.height) / 12.0, unit: UnitLength.feet)
let meters = measurement.converted(to: .meters).value
return LengthFormatter.imperialLengthFormatter.string(fromMeters: meters)
}
Of course, I had to implement the imperialLengthFormatter as well, but I did it as an extension to LengthFormatter itself, like this:
extension LengthFormatter {
public static let imperialLengthFormatter: LengthFormatter = {
let formatter = LengthFormatter()
formatter.isForPersonHeightUse = true
return formatter
}()
}
This actually doesn't kill performance as suggested in the comments for #maslova's answer. Due to the property being static, it only gets initialized once.
// When creating the Patient object
let patient = Patient(...) // Create in maanged object context
patient.height = 71
// Later displays in a collection view cell in a view controller
cell.heightLabel.Text = patient.heightInFeetString
Displays this in my table cell:
5 ft, 11 in
How to display Feet and Inches in SwiftUI
In case anyone arrives here looking for a SwiftUI answer.
struct MeasurementTestView: View {
#State private var height = Measurement(value: 68, unit: UnitLength.inches)
var body: some View {
VStack {
Text(height, format: .measurement(width: .narrow, usage: .personHeight))
Text(height, format: .measurement(width: .abbreviated, usage: .personHeight))
Text(height, format: .measurement(width: .wide, usage: .personHeight))
}
.font(.title)
}
}
Result
I'm displaying a distance with one decimal, and I would like to remove this decimal in case it is equal to 0 (ex: 1200.0Km), how could I do that in swift?
I'm displaying this number like this:
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
distanceLabel.text = String(format: "%.1f", distanceFloat) + "Km"
Swift 3/4:
var distanceFloat1: Float = 5.0
var distanceFloat2: Float = 5.540
var distanceFloat3: Float = 5.03
extension Float {
var clean: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
print("Value \(distanceFloat1.clean)") // 5
print("Value \(distanceFloat2.clean)") // 5.54
print("Value \(distanceFloat3.clean)") // 5.03
Swift 2 (Original answer)
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
distanceLabel.text = String(format: distanceFloat == floor(distanceFloat) ? “%.0f" : "%.1f", distanceFloat) + "Km"
Or as an extension:
extension Float {
var clean: String {
return self % 1 == 0 ? String(format: "%.0f", self) : String(self)
}
}
Use NSNumberFormatter:
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 2
// Avoid not getting a zero on numbers lower than 1
// Eg: .5, .67, etc...
formatter.numberStyle = .decimal
let nums = [3.0, 5.1, 7.21, 9.311, 600.0, 0.5677, 0.6988]
for num in nums {
print(formatter.string(from: num as NSNumber) ?? "n/a")
}
Returns:
3
5.1
7.21
9.31
600
0.57
0.7
extension is the powerful way to do it.
Extension:
Code for Swift 2 (not Swift 3 or newer):
extension Float {
var cleanValue: String {
return self % 1 == 0 ? String(format: "%.0f", self) : String(self)
}
}
Usage:
var sampleValue: Float = 3.234
print(sampleValue.cleanValue)
3.234
sampleValue = 3.0
print(sampleValue.cleanValue)
3
sampleValue = 3
print(sampleValue.cleanValue)
3
Sample Playground file is here.
Update of accepted answer for swift 3:
extension Float {
var cleanValue: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
usage would just be:
let someValue: Float = 3.0
print(someValue.cleanValue) //prints 3
To format it to String, follow this pattern
let aFloat: Float = 1.123
let aString: String = String(format: "%.0f", aFloat) // "1"
let aString: String = String(format: "%.1f", aFloat) // "1.1"
let aString: String = String(format: "%.2f", aFloat) // "1.12"
let aString: String = String(format: "%.3f", aFloat) // "1.123"
To cast it to Int, follow this pattern
let aInt: Int = Int(aFloat) // "1"
When you use String(format: initializer, Swift will automatically round the final digit as needed based on the following number.
You can use an extension as already mentioned, this solution is a little shorter though:
extension Float {
var shortValue: String {
return String(format: "%g", self)
}
}
Example usage:
var sample: Float = 3.234
print(sample.shortValue)
Swift 5
for Double it's same as #Frankie's answer for float
var dec: Double = 1.0
dec.clean // 1
for the extension
extension Double {
var clean: String {
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self)
}
}
Swift 5.5 makes it easy
Just use the new formatted() api with a default FloatingPointFormatStyle:
let values: [Double] = [1.0, 4.5, 100.0, 7]
for value in values {
print(value.formatted(FloatingPointFormatStyle()))
}
// prints "1, 4.5, 100, 7"
In Swift 4 try this.
extension CGFloat{
var cleanValue: String{
//return String(format: 1 == floor(self) ? "%.0f" : "%.2f", self)
return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(format: "%.2f", self)//
}
}
//How to use - if you enter more then two-character after (.)point, it's automatically cropping the last character and only display two characters after the point.
let strValue = "32.12"
print(\(CGFloat(strValue).cleanValue)
Formatting with maximum fraction digits, without trailing zeros
This scenario is good when a custom output precision is desired.
This solution seems roughly as fast as NumberFormatter + NSNumber solution from MirekE, but one benefit could be that we're avoiding NSObject here.
extension Double {
func string(maximumFractionDigits: Int = 2) -> String {
let s = String(format: "%.\(maximumFractionDigits)f", self)
var offset = -maximumFractionDigits - 1
for i in stride(from: 0, to: -maximumFractionDigits, by: -1) {
if s[s.index(s.endIndex, offsetBy: i - 1)] != "0" {
offset = i
break
}
}
return String(s[..<s.index(s.endIndex, offsetBy: offset)])
}
}
(works also with extension Float, but not the macOS-only type Float80)
Usage: myNumericValue.string(maximumFractionDigits: 2) or myNumericValue.string()
Output for maximumFractionDigits: 2:
1.0 → "1"
0.12 → "0.12"
0.012 → "0.01"
0.0012 → "0"
0.00012 → "0"
Simple :
Int(floor(myFloatValue))
NSNumberFormatter is your friend
let distanceFloat: Float = (currentUser.distance! as NSString).floatValue
let numberFormatter = NSNumberFormatter()
numberFormatter.positiveFormat = "###0.##"
let distance = numberFormatter.stringFromNumber(NSNumber(float: distanceFloat))!
distanceLabel.text = distance + " Km"
Here's the full code.
let numberA: Float = 123.456
let numberB: Float = 789.000
func displayNumber(number: Float) {
if number - Float(Int(number)) == 0 {
println("\(Int(number))")
} else {
println("\(number)")
}
}
displayNumber(numberA) // console output: 123.456
displayNumber(numberB) // console output: 789
Here's the most important line in-depth.
func displayNumber(number: Float) {
Strips the float's decimal digits with Int(number).
Returns the stripped number back to float to do an operation with Float(Int(number)).
Gets the decimal-digit value with number - Float(Int(number))
Checks the decimal-digit value is empty with if number - Float(Int(number)) == 0
The contents within the if and else statements doesn't need explaining.
This might be helpful too.
extension Float {
func cleanValue() -> String {
let intValue = Int(self)
if self == 0 {return "0"}
if self / Float (intValue) == 1 { return "\(intValue)" }
return "\(self)"
}
}
Usage:
let number:Float = 45.23230000
number.cleanValue()
Maybe stringByReplacingOccurrencesOfString could help you :)
let aFloat: Float = 1.000
let aString: String = String(format: "%.1f", aFloat) // "1.0"
let wantedString: String = aString.stringByReplacingOccurrencesOfString(".0", withString: "") // "1"
I am using NSDecimalNumber to format currency and want the following inputs and outputs:
9.99 --> 9.99
10 --> 10
10.00 --> 10
9.90 --> 9.90
9.9 --> 9.90
0 --> 0
0.01 --> 0.01
20 --> 20
10.01 --> 10.01
How can I do this in Swift.
EDIT: Essentially if there are cents (i.e. cents > 0) then display the cents. Otherwise, don't.
Your rule is "Display two fractional digits if either is non-zero; otherwise, display no fractional digits and no decimal point”. I would do it in the most straightforward way:
let number = NSDecimalNumber(string: "12345.00")
let formatter = NSNumberFormatter()
formatter.positiveFormat = "0.00"
let formattedString = formatter.stringFromNumber(number)!
.stringByReplacingOccurrencesOfString(".00", withString: "")
You can use NSNumberFormatter's currency formatting for this. However, there doesn't seem to be a built-in way to do rounding the way you want. Here's a workaround:
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
func numToCurrency (num: Double) -> String {
if floor(num) == num {
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
}
else {
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
}
return formatter.stringFromNumber(num)!
}
numToCurrency(9) // "$9"
numToCurrency(9.9) // "$9.90"
Check the NSNumberFormatter class reference for further configuration options (you might need to set a locale for this formatter to automatically use the correct international currency sign for the current user).
(Answering here, as a closed question was re-directed to this one...)
Perhaps the most straightforward route, particularly since this is tagged "Swift", is to determine if it's a whole number or not:
if value.truncatingRemainder(dividingBy: 1) == 0 {
// it's a whole number,
// so format WITHOUT decimal places, e.g. $12
} else {
// it's a fraction,
// so format WITH decimal places, e.g. $12.25
}
the added benefit is avoiding issues with locales and currency formats... no search/replace of ".00" when you're in Germany, for example, where the format is ",00"
edit/update: Xcode 8.3 • Swift 3.1
extension Formatter {
static let noFractionDigits: NumberFormatter = {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
formatter.minimumIntegerDigits = 1
return formatter
}()
static let twoFractionDigits: NumberFormatter = {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
formatter.minimumIntegerDigits = 1
return formatter
}()
}
extension FloatingPoint {
var customDescription: String {
return rounded(.down) == self ?
Formatter.noFractionDigits.string(for: self) ?? "" :
Formatter.twoFractionDigits.string(for: self) ?? ""
}
}
extension String {
var double: Double { return Double(self) ?? 0 }
}
let array = ["9.99","10","10.00","9.90","9.9"]
let results = array.map { $0.double.customDescription }
results // ["9.99", "10", "10", "9.90", "9.90"]
Here's how to create a custom formatter class to handle this for you:
import Foundation
class CustomFormatter: NSNumberFormatter {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
self.locale = NSLocale.currentLocale()
self.numberStyle = .DecimalStyle
}
func isIntegerNumber(number:NSNumber) -> Bool {
var value: NSDecimal = number.decimalValue
if NSDecimalIsNotANumber(&value) { return false }
var rounded = NSDecimal()
NSDecimalRound(&rounded, &value, 0, NSRoundingMode.RoundPlain)
return NSDecimalCompare(&rounded, &value) == NSComparisonResult.OrderedSame
}
override func stringFromNumber(number: NSNumber) -> String? {
if isIntegerNumber(number) {
self.minimumFractionDigits = 0
self.maximumFractionDigits = 0
return super.stringFromNumber(number)
}
else {
self.minimumFractionDigits = 2
self.maximumFractionDigits = 2
return super.stringFromNumber(number)
}
}
}
let formatter = CustomFormatter()
formatter.stringFromNumber(NSDecimalNumber(double: 5.00)) // -> "5"
formatter.stringFromNumber(NSDecimalNumber(double: 5.01)) // -> "5.01"
formatter.stringFromNumber(NSDecimalNumber(double: 5.10)) // -> "5.10"
Thanks to this post for the proper way to test if a NSDecimal is an integer.
I think it's best to let the currencyStyle determine the maximumFractionDigits. Just set the minimumFractionDigits to 0 where desired. The code is slightly shorter, but as a bonus if you set the locale, this way will allow for languages that don't have 2 decimal places.
Using NSNumberFormatter gives you the benefit of currency symbols, decimal places and comma’s, all in the perfect places for the different locale’s.
extension NSNumber {
func currencyString() -> String? {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
if self.isEqualToNumber(self.integerValue) {
formatter.minimumFractionDigits = 0
}
return formatter.stringFromNumber(self)
}
}
let inputArray: [NSDecimalNumber] = [9.99, 10, 10.00, 9.90, 0, 0.01, 20, 10.01, 0.5, 0.055, 5.0]
let outputArray: [String] = inputArray.map({return $0.currencyString() ?? "nil"})
print(outputArray)
["$9.99", "$10", "$10", "$9.90", "$0", "$0.01", "$20", "$10.01", "$0.50", "$0.06", "$5"]
Adding a locale to a NSNumberFormatter looks like this(ex. from an SKProduct object):
formatter.locale = product!.priceLocale
For an OSX app you need to add:
formatter.formatterBehavior = .Behavior10_4