Reputation: 869
I've crated an Enum with String type. It has two init methods. One is default init method with rawValue and another one is custom init method with intValue. I've written it like this. Is there any simple way to not to use two switch cases?
enum Roman: String {
case I,V,X,L,C,D,M
var intValue: Int {
switch self {
case .I:
return 1
//...
}
}
init?(intValue: Int) {
switch intValue {
case 1:
self = .I
//...
default:
return nil
}
}
}
//Roman to Int
let number = "XXI".reversed()
.map { Roman(rawValue: String($0))?.intValue ?? 0 }
.reduce((total: 0, max: 0)) { result, value in
let newTotal = result.total + (value < result.max ? -value : value)
return (newTotal, max(result.max, value))
}.total
Upvotes: 2
Views: 5519
Reputation: 299565
There's nothing wrong with Dávid Pásztor's answer, but I do really like Βασίλης Δ.'s thinking raw values. That just seems a very natural approach. So I'd like to bring those's together.
First, starting with Βασίλης Δ.'s code, adding an intValue
alias just because I think it reads a bit better.
enum Roman: Int {
case I = 1
case V = 5
case X = 10
case L = 50
case C = 100
case D = 500
case M = 1000
var intValue: Int { return rawValue }
}
Then provide a lookup for Strings using the new CaseIterable:
extension Roman: CaseIterable {
enum Error: Swift.Error {
case invalid
}
init<S: StringProtocol>(_ string: S) throws {
guard let roman = Roman.allCases.first(where: { "\($0)" == string }) else {
throw Error.invalid
}
self = roman
}
init(_ character: Character) throws { try self.init(String(character)) }
}
With that, I think the number
algorithm gets a little bit nicer at the top:
let number = try "XXI".reversed()
.map { try Roman($0).intValue }
.reduce((total: 0, max: 0)) { result, value in
let newTotal = result.total + (value < result.max ? -value : value)
return (newTotal, max(result.max, value))
}.total
I'm not a big fan of this algorithm, because it behaves erratically for invalid input, but at least this version rejects invalid characters rather than converting them to 0.
Upvotes: 3
Reputation: 54755
You can get rid of the switch
statements by defining two dictionaries for a bidirectional mapping between the Int
values and the enum case
s.
enum Roman: String {
case I, V, X, L, C, D, M
private static let intValues:[Roman:Int] = [.I:1,.V:5,.X:10,.L:50,.C:100,.D:500,.M:1000]
private static let mappingDict:[Int:Roman] = Dictionary(uniqueKeysWithValues: Roman.intValues.map({ ($1, $0) }))
var intValue:Int {
return Roman.intValues[self]!
}
init?(intValue:Int){
guard let roman = Roman.mappingDict[intValue] else { return nil }
self = roman
}
}
Upvotes: 7
Reputation: 1456
If I understood correctly what you want.. Why don't assign directly the values to the enumerator cases? Ex.
enum Roman: Int {
case I = 1
case V = 5
case X = 10
case L = 50
case C = 100
case D = 500
case M = 1000
}
And on your main class
print(Roman.I.rawValue)
print(Roman(rawValue: 1))
Upvotes: 0