How to throw error in map for converting string to int array for number format exception - swift2

I have a string
var str = "1 2 3 4"
and I want to convert it into [Int]. It can be done as follow
let intArray = str.characters.split {$0 == " "}.map(String.init).map { Int($0)!}
Now what if my string is
var invalid = " 1 a 4"
Then, the program will crash with
fatal error: unexpectedly found nil while unwrapping an Optional value
I need to be able to check number and throw number format error in map.

You can use throws, try - throw, do - try - catch and guard (if) for that. Here is the code
var invalid = " 1 a 4"
let intArray: [Int]
do {
intArray = try getIntArray(invalid, delimiter: " ")
}catch let error as NSError {
print(error)
intArray = []
}
func getIntArray(input:String, delimiter:Character ) throws -> [Int] {
let strArray = input.characters.split {$0 == delimiter}.map(String.init)
let intArray = try strArray.map {
(int:String)->Int in
guard Int(int) != nil else {
throw NSError.init(domain: " \(int) is not digit", code: -99, userInfo: nil)
}
return Int(int)!
}
return intArray
}
In getIntArray function, We first convert the input string to string array.
Then when we are converting string array to int array, we are expanding the map closure function parameter to include number format checking and throwing error using "guard".
"guard" can be replaced with "if" too if it is not available
if Int(int) == nil {
throw NSError.init(domain: " \(int) is not digit", code: -99, userInfo: nil)
}

Rather than throwing NSError types, you can create your own Swift native enum conforming to ErrorType where your enumeration contains the error case you would like to explicitly handle. E.g.
enum MyErrors : ErrorType {
case NumberFormatError(String)
}
/* throwing function attempting to initialize an
integer given a string (if failure: throw error) */
func strToInt(str: String) throws -> Int {
guard let myInt = Int(str) else { throw MyErrors.NumberFormatError(str) }
return myInt
}
Example usage within a do-try-catch construct:
func strAsIntArr(str: String) -> [Int]? {
var intArr: [Int] = []
do {
intArr = try str.characters.split {$0 == " "}
.map(String.init)
.map { try strToInt($0) }
} catch MyErrors.NumberFormatError(let faultyString) {
print("Format error: '\(faultyString)' is not number convertible.")
// naturally you could rethrow here to propagate the error
} catch {
print("Unknown error.")
}
return intArr
}
/* successful example */
let myStringA = "1 2 3 4"
let intArrA = strAsIntArr(myStringA)
/*[1, 2, 3, 4] */
/* error throwing example */
let myStringB = "1 b 3 4"
let intArrB = strAsIntArr(myStringB)
/* [], Format error: 'b' is not number convertible. */

Related

Generic parameter 'Self' could not be inferred (swift 4)

I want to make a substring extension for string , I tried below 2 ways but unsuccessful:
extension String
{
func substringToFirstChar(of char: Character) -> String
{
let pos = self.range(of: String(char))
let subString = self[..<pos?.lowerBound]
return String(subString)
}
}
or
extension String
{
func substringToFirstChar(of char: Character) -> String
{
let pos = self.index(of: char)
let subString = self[..<pos]
return String(subString)
}
}
xcode prompt error: Generic parameter 'Self' could not be inferred at the "let subString = self[.." line.
How to do that ?
range(of / index(of returns an optional. You have to unwrap the optional in the range expression
extension String
{
func substringToFirstChar(of char: Character) -> String?
{
guard let pos = self.range(of: String(char))?.lowerBound else { return nil }
// or guard let pos = self.index(of: char) else { return nil }
let subString = self[..<pos]
return String(subString)
}
}
alternatively – to avoid the optional – return the unchanged string if there is no match
extension String
{
func substringToFirstChar(of char: Character) -> String
{
guard let pos = self.range(of: String(char))?.lowerBound else { return self }
// or guard let pos = self.index(of: char) else { return self }
let subString = self[..<pos]
return String(subString)
}
}

How to use thousand separator swift

func showNumbers(){
if let inputString = numberInput.text {
let input = Int(inputString)
let nums = input?.formattedWithSeparator
let group = Int(round(groupslider.value))
let priceEach = Int(round(Double((nums)!/group*100))/100)
perperson.text = String(priceEach)
}
}
}
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = " "
formatter.numberStyle = .decimal
return formatter
}()
}
extension BinaryInteger {
var formattedWithSeparator: String {
return Formatter.withSeparator.string(for: self) ?? ""
}
}
I have two places that I want to make it like 1,000,000
input String and perperson.text
what should I use? NSNumberForatter?
I want to use thousandSeparator or groupSeparator.
I get " Binary operator '/' cannot be applied to operands of type 'String' and 'Int' " this error message.

How to remove multiple spaces in Strings with Swift 2

Until Swift 2 I used this extension to remove multiple whitespaces:
func condenseWhitespace() -> String {
let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).filter({!Swift.isEmpty($0)})
return " ".join(components)
}
but with Swift 2 now I get the error
Cannot invoke 'isEmpty' with an argument list of type '(String)'
How could I now remove multiple spaces with Swift 2?
Thnx!
In Swift 2, join has become joinWithSeparator and you call it on the array.
In filter, isEmpty should be called on the current iteration item $0.
To replace whitespaces and newline characters with unique space characters as in your question:
extension String {
func condenseWhitespace() -> String {
let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
return components.filter { !$0.isEmpty }.joinWithSeparator(" ")
}
}
let result = "Hello World.\nHello!".condenseWhitespace() // "Hello World. Hello!"
Because your function does not take any parameter you could make it a property instead:
extension String {
var condensedWhitespace: String {
let components = self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
return components.filter { !$0.isEmpty }.joinWithSeparator(" ")
}
}
let result = "Hello World.\nHello!".condensedWhitespace // "Hello World. Hello!"
In Swift 3 there's even more changes.
Function:
extension String {
func condenseWhitespace() -> String {
let components = self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
}
let result = "Hello World.\nHello!".condenseWhitespace()
Property:
extension String {
var condensedWhitespace: String {
let components = self.components(separatedBy: NSCharacterSet.whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
}
let result = "Hello World.\nHello!".condensedWhitespace
In Swift 4.2 NSCharacterSet is now CharacterSet, and you can omit and use dot syntax:
extension String {
func condenseWhitespace() -> String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
}
let result = "Hello World.\nHello!".condenseWhitespace() // "Hello World. Hello!"
Split string to array and then join again in not memory efficient. Its Takes lot of memory. The best way in this case is to scan the given string and perform operations on that. Regular Expression is the advance way to scan a text. For the above conclusion the the solution is given below:
Swift 4.x
extension String {
func removeExtraSpaces() -> String {
return self.replacingOccurrences(of: "[\\s\n]+", with: " ", options: .regularExpression, range: nil)
}
}
Usages
let startingString = "hello world! \n\n I am here!"
let processedString = startingString.removeExtraSpaces()
print(processedString)
Output:
processedString => "hello world! I am here!"
You can Do more according to your own requirements but thing I am pointing out here is to use regular expressions with string rather then create arrays which will consume lot of memory.
Cleanest version. Documented, memory efficient, extremely easy to use.
extension String {
/// Returns a condensed string, with no extra whitespaces and no new lines.
var condensed: String {
return replacingOccurrences(of: "[\\s\n]+", with: " ", options: .regularExpression, range: nil)
}
/// Returns a condensed string, with no whitespaces at all and no new lines.
var extraCondensed: String {
return replacingOccurrences(of: "[\\s\n]+", with: "", options: .regularExpression, range: nil)
}
}
Usage:
let a = " Hello\n I am a string ".condensed
let b = " Hello\n I am a string ".extraCondensed
Output:
a: "Hello I am a string"
b: "HelloIamastring"
SWIFT 3: Cleaner version
extension String {
var condensedWhitespace: String {
let components = self.components(separatedBy: .whitespacesAndNewlines)
return components.filter { !$0.isEmpty }.joined(separator: " ")
}
}
Here is mine: How it's actually worked.
extension String {
func removeExtraSpaces() -> String {
var data = ""
var numberOfSpace = 0
let items = self.getComponents(separatedBy: " ")
for item in items{
if item == " "{
numberOfSpace = numberOfSpace + 1
}else{
numberOfSpace = 0
}
if numberOfSpace == 1 || numberOfSpace == 0 {
data = data + item
//data.append(item)
}
}
return data
}
}
Usages
var message = "What is the purpose of life?"
message = message.removeExtraSpaces()
print(message)
Output:
What is the purpose of life?
var str = "Hello World.\nHello!"
if let regex = try? NSRegularExpression(pattern: "\\s+", options:NSRegularExpression.Options.caseInsensitive)
{
str = regex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.count), withTemplate: " ")
}

Cannot assign a value of type 'String!' to a value of type 'AnyObject?' - Xcode and parse

I am running the following code:
var inventario = PFQuery(className:"InventarioObjetos")
inventario.getObjectInBackgroundWithId(toPassID) {
(inventario: PFObject?, error: NSError?) -> Void in
if error != nil {
NSLog("%#", error!)
} else {
inventario["Categoria"] = self.nuevoCategoria.text
inventario["Descripcion"] = self.nuevoDescripcion.text
inventario["Tamano"] = self.tamano.text
// Guarda la fecha de entrada
let dateStringFormatter = NSDateFormatter()
dateStringFormatter.dateFormat = "dd-MM-yyyy"
dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
let d = dateStringFormatter.dateFromString(self.nuevoFechaDeEntrada.text)
inventario["FechaDeEntrada"] = d
}
}
And on the inventario["Tamano"] = self.tamano.text line, I get this error:
Cannot assign a value of type 'String!' to a value of type 'AnyObject?'
Just cast self.tamano.text to an AnyObject: inventario["Tamano"] = self.tamano.text as! AnyObject
try this:
inventario["Tamano"] = self.tamano.text? (as! AnyObject?) shouldn't need this
Actually I would add the "?" in all your conversions just in case the user left one empty or a check like
if let strTamano = self.tamano.text{
inventario["Tamano"] = strTamano
} else { println("error converting tamano") }
of course the println would popup a dialog or do something more useful to the final user.

Saving / Loading Images in Postgres using Anorm (Scala/PlayFramework 2)

I think I'm saving the image to Postgres correctly, but get unexpected results trying to load the image. I don't really know if the error is in save or load.
Here is my Anorm code for saving the image:
def storeBadgeImage(badgeHandle: String, imgFile: File) = {
val cmd = """
|update badge
|set img={imgBytes}
|where handle = {badgeHandle}
"""
var fis = new FileInputStream(imgFile)
var imgBytes: Array[Byte] = Resource.fromInputStream(fis).byteArray
// at this point I see the image in my browser if I return the imgBytes in the HTTP response, so I'm good so far.
DB.withConnection { implicit c =>
{
try {
SQL(cmd stripMargin).on("badgeHandle" -> badgeHandle, "imgBytes" -> imgBytes).executeUpdate() match {
case 0 => "update failed for badge " + badgeHandle + ", image " + imgFile.getCanonicalPath
case _ => "Update Successful"
}
} catch {
case e: SQLException => e.toString()
}
}
}
}
...I get "update succesful", so I presume the save is working (I could be wrong). Here is my code for loading the image:
def fetchBadgeImage(badgeHandle: String) = {
val cmd = """
|select img from badge
|where handle = {badgeHandle}
"""
DB.withConnection { implicit c =>
SQL(cmd stripMargin).on("badgeHandle" -> badgeHandle)().map {
case Row(image: Array[Byte]) => {
"image = " + image
}
case Row(Some(unknown: Any)) => {
println(unknown + " unknown type is " + unknown.getClass.getName) //[B#11be1c6 unknown type is [B
"unknown"
}
}
}
}
...rather than going into the case "Row(image: Array[Byte])" as hoped, it goes into the "Row(Some(unknown: Any))" case. My println outputs "[B#11be1c6 unknown type is [B"
I don't know what type [B is or where I may have gone wrong...
It's an array of byte in Java(byte[]). > "I don't know what type [B".
And You can write match { case Row(Some(image: Array[Byte])) => } too in this case and that might be better.
Or you might be able to do that as follows.
val results: Stream[Array[Byte]] = SQL(cmd stripMargin)
.on("badgeHandle" -> "name")().map { row => row[Array[Byte]]("img") }
...Oops, got the following compile error.
<console>:43: error: could not find implicit value for parameter c: anorm.Column[Array[Byte]]
val res: Stream[Array[Byte]] = SQL(cmd stripMargin).on("badgeHandle" -> "name")().map { row => row[Array[Byte]]("img") }
Unfortunately, scala.Array is not supported by default. If you imitate the way of other types, It works.
implicit def rowToByteArray: Column[Array[Byte]] = {
Column.nonNull[Array[Byte]] { (value, meta) =>
val MetaDataItem(qualified, nullable, clazz) = meta
value match {
case bytes: Array[Byte] => Right(bytes)
case _ => Left(TypeDoesNotMatch("..."))
}
}
}
val results: Stream[Array[Byte]] = SQL(cmd stripMargin)
.on("badgeHandle" -> "name")().map { row => row[Array[Byte]]("img") }
https://github.com/playframework/Play20/blob/master/framework/src/anorm/src/main/scala/anorm/Anorm.scala

Resources