range swift
Trate un solo valor entero como un rango en Swift (2)
Necesito validar la longitud de una cadena. Los valores permitidos para el recuento de caracteres son:
- 6 - 9 caracteres
- 12 caracteres
- 15 caracteres
Todas las cadenas con un recuento de caracteres diferente no son válidas. Por lo tanto, me gustaría crear una función Swift que acepte varios rangos y evalúe la cadena:
extension String {
func evaluateLength(validCharacterCounts: Range<Int>...) -> Bool {
// Implementation
}
}
Ahora puedo llamar a la función para un único rango Int
:
"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10)
y múltiples rangos Int
:
"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10, 15..<20)
Pero no puedo llamar a la función con valores enteros únicos y aislados:
"Live long and prosper".evaluateLength(validCharacterCounts: 6..<10, 12, 15)
porque 12
y 15
se escriben como Int
y no como Range<Int>
.
Error de compilación Swift: no se puede convertir el valor del tipo ''Int'' al tipo de argumento esperado ''Rango''
¿Hay alguna forma de tratar un Entero único como un Range
en Swift, como lanzarlo automáticamente a Range<Int>
?
(Después de que 5
es equivalente a 5..<6
, matemáticamente hablando, 5
es un rango).
Así es como lo haría. En lugar de ocuparme de la semántica del rango, solo estoy creando una extensión Establecer con un inicializador relevante.
extension Set where Element == Int {
init(with elements: [Int] = [], and ranges: Range<Int>...) {
var allElements = [elements]
ranges.forEach {
allElements.append(Array($0.lowerBound..<$0.upperBound))
}
self.init(allElements.joined())
}
}
let newSet = Set(with: [1, 3, 328], and: 6..<10, 15..<20)
newSet.contains(3) //true
newSet.contains(4) //false
newSet.contains(16) //true
Esto le permite pasar solo valores únicos, solo rangos o ambos. La única advertencia es los params nombrados que los separan.
La semántica Set
también significa que puede crear constantes estáticas si así lo desea y también debe ser significativamente más rápido.
Se me ocurrieron 3 soluciones pero ninguna de ellas es tan agradable como sería si el método acepta Int
o Range<Int>
pero aquí vamos?
1 - Tiene seguridad tipo pero tiene que usar algunas soluciones:
extension String {
func evaluateLength(validCharacterCounts: [Range<Int>]? = nil) -> Bool {
if let validCharCounts = validCharacterCounts {
for validCharCount in validCharCounts {
if validCharCount.contains(characters.count) {
return true
}
}
}
return false
}
}
extension Int {
var asRange: Range<Int> {
return self..<self+1 as Range<Int>
}
}
"Hello, playground".evaluateLength(validCharacterCounts: [17.asRange, 1, 25, 1..<45]) // true
2 - No tiene seguridad de tipo:
extension String {
func evaluateLength(validCharacterCounts: [Any]? = nil) -> Bool {
if let validCharCounts = validCharacterCounts {
for validCharCount in validCharCounts {
if let range = validCharCount as? Range<Int> {
if range.contains(characters.count) {
return true
}
} else if let range = validCharCount as? CountableRange<Int> {
if range.contains(characters.count) {
return true
}
} else if let range = validCharCount as? CountableClosedRange<Int> {
if range.contains(characters.count) {
return true
}
} else if let int = validCharCount as? Int {
if int.asRange.contains(characters.count) {
return true
}
} else {
fatalError("Unexpected type: /(type(of: validCharCount))")
}
}
}
return false
}
}
extension Int {
var asRange: Range<Int> {
return self..<self+1 as Range<Int>
}
}
"Hello, playground".evaluateLength(validCharacterCounts: [12, 1, 4, 6, 14...18]) // true
3 - La mejor solución hasta ahora, pero aún no perfecta:
extension String {
func evaluateLength(validCharacterCounts: [Int]? = nil) -> Bool {
guard let validCharCounts = validCharacterCounts else {
return false
}
return validCharCounts.contains(characters.count)
}
}
"Hello, playground".evaluateLength(validCharacterCounts: [12, 1, 4, 6] + Array(14...18)) // true