I was wondering what the best approach would be to separate multiple items from a string in swift. I'm hoping to separate a unit and an amount from a string and then use those values to create an object of my ingredient class.
For example:
var string = "4 cups sugar"
I would need to grab the 4 (amount) and convert it to an int and then grab the unit (cups)
var amount = 4
var unit = "cups"
Another example:
"3 large eggs"
In this case I would want to pull out the 3 (amount) and the unit would be empty.
var amount = 3
var unit = ""
Then I would create my object using the unit and amount values.
I'm still a novice at swift, and more-so with string manipulation so I'm not entirely sure how to approach this, any help in the right direction would be great.
I am working with an ingredient class that is structured as:
class IngredientModel {
var amount = 0
var unit = ""
init(amount : Int, unit : String) {
self.amount = amount
self.unit = unit
}
In SWIFT you can do this early with optionals & generics. I can show you how? with your example..
Your Ingredient class
class IngredientModel {
var amount = 0
var unit = ""
let units = ["cups","spoon"] //Add your required units
init(string: String) {
let array = string.components(separatedBy: " ")
if let _amount = array.ref(0) {
self.amount = Int(_amount) ?? 0
}
if let _unit = array.ref(1),
units.contains(_unit){
self.unit = _unit
}
}
}
An Array Extension to prevent index out of range
extension Array {
func ref(_ i:Int) -> Element? {
return 0 <= i && i < count ? self[i] : nil
}
}
If I'm reading the apple documentation right, you should use the components(separatedBy) function. The parameter should be one space in quotes like so:
let array = yourString.components(separatedBy: " ")
Related
I'm currently teaching myself Swift coming from a background of Python recently and Visual Basic originally. I would describe myself as competent in those codes but a novice in Swift. I'm trying to write a function that will return a set number of digits from either the start or end of a long integer. My chosen method has been to convert the integer to a string and then use the prefix or suffix command.
Whilst I can get the function to work if it has no flow control and uses either prefix or suffix (first lot of code), when I try to write one function that does both I get an unresolved identifier error on the turnStringToInteger variable (second lot of code). I'm pretty sure this is because the variable lives within the if {} but if I declare it outside of the if loop (hashed out) this also errors. I appreciate this will have a really simple answer but how do I use the return correctly with a nested if loop?
This works...
//Function to Trim Integer (Prefix Only)
func integerTrim(integer:Int, trimLength:Int) -> Int {
var strFromInteger = String(integer)
var trimmedString = strFromInteger.prefix(trimLength)
var intFromString = Int(trimmedString) ?? 0
return intFromString
}
//Declare Input
var inputInt = 12345678910
//Call Function
var result = integerTrim(integer: inputInt, trimLength: 4)
//Print Results
print(inputInt)
print(result)
This doesn't...!
//Function to trim integer prefix or suffix
func integerTrim(integer:Int, type:String, trimLength:Int) -> Int {
var typeID = type
//var turnStringToInteger: Int
if typeID == "P" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.prefix(trimLength)
var turnStringToIngeger = Int(trimmedString) ?? 0
}
else if typeID == "S" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.suffix(trimLength)
var turnStringToIngeger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}
//Declare Input
var inputInt = 53737363856453
//Call Function
var result = integerTrim(integer: inputInt, type: "P", trimLength: 4)
//Print Results
print(inputInt)
print(result)
As I am self taught I appreciate I may also not be using best practices. I really want to learn to do this properly so if I am going about all of this the wrong way to begin with I would be equally happy to hear other approaches. For example I did consider turning the integer to an array and then creating the trimmed integer from positions within this array. Would this be more elegant?
If you want to access the variable outside of the scope where it is assigned, you need to declare it in the outer scope.
If you do that without assigning it an initial value, you get an error: variable 'turnStringToInteger' used before being initialized. That happens because Swift sees a path in which turnStringToInteger never gets assigned a value (imagine what happens if "X" is passed in for type).
So your real issue is the use of String as the type for type. It would be better to use an enum that expresses exactly what you want:
enum TrimType {
case prefix, suffix
}
func integerTrim(integer: Int, type: TrimType, trimLength: Int) -> Int {
let typeID = type
var turnStringToInteger: Int
switch typeID {
case .prefix:
let turnIntegerToString = String(integer)
let trimmedString = turnIntegerToString.prefix(trimLength)
turnStringToInteger = Int(trimmedString) ?? 0
case .suffix:
let turnIntegerToString = String(integer)
let trimmedString = turnIntegerToString.suffix(trimLength)
turnStringToInteger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}
Now there are only 2 possibilities for type and the switch handles both.
You call it like this:
let result = integerTrim(integer: inputInt, type: .prefix, trimLength: 4)
... after a little refactoring:
func integerTrim(integer: Int, type: TrimType, trimLength: Int) -> Int {
let turnIntegerToString = String(integer)
let trimmedString: Substring
switch type {
case .prefix:
trimmedString = turnIntegerToString.prefix(trimLength)
case .suffix:
trimmedString = turnIntegerToString.suffix(trimLength)
}
return Int(trimmedString) ?? 0
}
There's a few ways to do this. The root of the problem is your turnIntegerToString lifetime is within the braces - and the return is outside the braces.
func integerTrim(integer:Int, type:String, trimLength:Int) -> Int {
var typeID = type
var turnStringToInteger: Int = 0
// If you don't want to assign it to zero (your nil coalesce implies we can) - instead use...
// var turnStringToInteger: Int! // However - this can crash since your if statement does not cover all situations
if typeID == "P" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.prefix(trimLength)
turnStringToIngeger = Int(trimmedString) ?? 0
}
else if typeID == "S" {
var turnIntegerToString = String(integer)
var trimmedString = turnIntegerToString.suffix(trimLength)
turnStringToIngeger = Int(trimmedString) ?? 0
}
return turnStringToInteger
}
var object1 = "C_active.scn"
var object86 = "Soap.scn"
var object41 = "image.scn"
var object9 = "NaCl.scn"
Name of different .SCN files
public func addBox(sceneView: ARSCNView) {
let imagePlaneScene = SCNScene(named: "art.scnassets/" + object1)
let imagePlaneNode = imagePlaneScene?.rootNode.childNode(withName: "object1", recursively: true)
imagePlaneNode?.position = positioner
I have a code reader that gives me a number and from that Int I have to place a specific .SCN file. I don't want to add 100 if statements like I do below. Is the some way to concatenate a string with a Int and turn that into a var in swift? (The numbers after each object is the number I receive from my code reader)
if(coding == 1) {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
} else if(coding == 2) {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
} else {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
}
Something like
var("object" + coding) -> coding41 (Var)
Why don't you just use a dictionary to store your file names?
var object: [Int: String] = [1: "C_active.scn", 9: "NaCl.scn" ...]
When you need a particular filename, just use the number key attached to that string.
print(object[9]) //Prints "NaCl.scn"
Say I have a String, how do I determine the number of words in it? I'm trying to create an extension like:
extension String {
var numberOfWords: Int {
// Insert string-counting code here
}
}
If you search "word count string swift" you'll find dozens of StackOverflow answers and gists that tell you to split the string using str.components(separatedBy: " ").count.
DON'T USE components(separatedBy:)!!!
Many non-European languages (particularly East Asian languages) don't use spaces to split words. This will also incorrectly count hyphenated words as separate, and lone punctuation as a word.
The most correct AND most performant way to solve this problem is to use either enumerateSubstrings(in:options:) or CFStringTokenizer.
// enumerateSubstrings
extension String {
var numberOfWords: Int {
var count = 0
let range = startIndex..<endIndex
enumerateSubstrings(in: range, options: [.byWords, .substringNotRequired, .localized], { _, _, _, _ -> () in
count += 1
})
return count
}
}
OR:
// CFStringTokenizer
extension String {
var numberOfWords: Int {
let inputRange = CFRangeMake(0, utf16.count)
let flag = UInt(kCFStringTokenizerUnitWord)
let locale = CFLocaleCopyCurrent()
let tokenizer = CFStringTokenizerCreate(kCFAllocatorDefault, self as CFString, inputRange, flag, locale)
var tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
var count = 0
while tokenType != [] {
count += 1
tokenType = CFStringTokenizerAdvanceToNextToken(tokenizer)
}
return count
}
}
Both are very performant, but enumerateSubtrings(in:options:...) is about twice as fast.
Shocked that nobody is pointing this out elsewhere, so I hope people searching for a solution find this.
Count of words in a string
Create an extension of String
extension String{
var wordCount:Int{
let chararacter = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
let comps = components(separatedBy: chararacter)
let words = comps.filter { !$0.isEmpty }
return words.count
}
}
How to use
"This is a test string".wordCount // Result: 5
I'm working with Swift 3.
I would like to have this C syntax :
int myVar;
int *pointer = &myVar;
So modifying pointer or myVar does the same exact same thing.
Also I don't know if it makes any difference, but in my case myVar is an array containing elements of a class and pointer is a pointer to one element of this array.
The & also exists in Swift but can only be used as part of a parameter list (e.g. init, func, closure).
var i = 5
let ptr = UnsafeMutablePointer(&i)
print(ptr.pointee) // 5
// or
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
ptr.initialize(to: 5)
// or with a closure
let ptr: UnsafePointer = { $0 }(&i)
(Assuming I understand what you're asking for....)
Try the following code in a playground. It should print "99" three times.
class Row {
var rowNumber = 0
}
var rows = [Row]()
let testRow = Row()
testRow.rowNumber = 1
rows.append(testRow)
let selectedRow = rows[0]
selectedRow.rowNumber = 99
print(testRow.rowNumber)
print(selectedRow.rowNumber)
print(rows[0].rowNumber)
By default, there's no copying of objects as part of an assignment statement. If it were a struct, that would be different.
Adding a bit for completeness:
If you want a similar effect with scalar values instead of objects, Swift supplies various types of wrappers.
let intPointer = UnsafeMutablePointer<Int>.allocate(capacity: 8) // Should be 1, not 8 according to comment re: docs
let other = intPointer
other.pointee = 34
print(intPointer.pointee)
(Warning: I haven't used these wrappers for anything except experimenting in a playground. Don't trust it without doing some research.)
Same example as #Phillip. But I used struct. In this example rows[0] won't change:
struct Row {
var rowNumber = 0
}
var rows = [Row]()
var testRow = Row()
testRow.rowNumber = 1
rows.append(testRow)
var selectedRow = rows[0]
selectedRow.rowNumber = 99
print(testRow.rowNumber) // prints 1
print(selectedRow.rowNumber) // prints 99
print(rows[0].rowNumber) // prints 1
There are no C style pointers (Unsafe Pointer) as the question asks however objects are shared by reference and structures are by value:
Swift assign, pass and return a value by reference for reference type and by copy for Value Type
structures are always copied when they are passed around in your code, but classes are passed by reference.
For example
How to have pointers/ references to objects
class Song {
init(title: String, image: String, file: String, volume: Float, queuePlayer: AVQueuePlayer, playerLooper: AVPlayerLooper?) {
self.title = title
self.image = image
...
}
var title: String
var image: String
...
}
var aSong = Song(title: "", image: "", ...)
var arrOfSongReferences: [Song] = [Song]()
arrOfSongReferences.append(aSong)
var ptrToASong: Song = aSong
aSong = nil
// Due to Swift garbage collection ARC (Automatic Reference Counting), we still have references to the original aSong object so it won't be deleted
If data is struct you cannot do this
struct Song {
var title: String
var image: String
...
}
var aSong: Song = Song(title: "", image: "", ...)
var copyOfASong: Song = aSong
Method
You can also pass by reference into a function
// this would be inside a class, perhaps Player. It doesn't have to be a static btw
static func playSound(_ sound: inout Song, volume: Float = 0.0) {
if (sound.playerLooper == nil) {
...
}
}
// usage
Player.playSound(sound: &aSong)
Trying to run a simple calculation via a function in my class. I simply want to add bill1 + bill2 and print the total amount spent on bills. So (bill1 + bill2 = total). And then print the total amount.
Current error states - "Code after 'return' will never be executed." Now, is my location for my print in the wrong location or did I declare my variables incorrectly? Should I be using vars instead of lets?
What do you recommend for my function in order to calculate and print the result?
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
//totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
*Error println("You spend \(totalBillsPerMonth)")
}
}
First: "Code after 'return' will never be executed."
Yes it will not, after you call return you exit the function and return to the function that call it, you probably have an warning in XCode warning you about telling you that
Second: "Should I be using vars instead of lets"
If the value changes you MUST use var, if it does not you SHOULD use let.
Some problems I can see in your code:
class BillsCalculator
{
//use _ in the beginning of the name for class variables
//eg. _nameOfBill instead nameOfBill1
//It is not wrong use nameOfBill1 is just not recommended
//if nameOfBill1 change use var
let nameOfBill1: String = "Medical"
//Why is this declare twice
let nameOfBill1: String = "Hulu"
//Those values look like change should be var
var monthlyBillAmount1: Double = 34.25
var monthlyBillAmount2: Double = 7.99
var calculateTotalsPerMonth: Double = 0.0
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
//print before return
println("You spend \(totalBillsPerMonth)")
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
}
}
Here you should either print the value of your total bill or return that value. As you just want to print the total bill amount so I would recommend you to just print, not to return anything. You can refer the below code.
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
calculateTotalsPerMonth= add(monthlyBillAmount1 + monthlyBillAmount2)
println("You spend : "+totalBillsPerMonth);
}
}
One tiny error in your code
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
These two variables have the same name, perhaps one should be:
let nameOfBill2: String = "Hulu"
And yes return is always the last line in the function, so any codes after return will never be executed. If you only want to get the total of two bills, you can simply do this:
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double {
//println("You spend \(totalBillsPerMonth)")
return monthlyBillAmount1 + monthlyBillAmount2
}
and call this function with your bill variables, like:
let bill1 = 34.25
let bill2 = 7.99
let totalBill = calculateTotalsPerMonth(bill1, bill2)
println("You spent \(totalBill)")
Swift is a very smart language, and it is type safe. You can remove the type if you want, more like a personal programming style thing.
let bill1: Double = 34.25
let bill1 = 34.25
They both will be type "Double"
As others have said, you need to put your println statement before return since returns ends the execution of the method; thus println will never be run.
However, I would suggest a few changes to your current approach:
// A bill is an object - why not encapsulate it in a struct.
struct Bill {
let name: String
let amount: Double
}
// Using structs is generally preferred, unless you need inheritance and/or
// references to your BillsCalculator objects.
struct BillsCalculator {
let bill1: Bill
let bill2: Bill
// Using a read-only computed property means you don't need to set
// the total to have an initial value of zero.
var totalBilled: Double {
return bill1.amount + bill2.amount
}
}
// Since you're probably going to want to reuse BillsCalculator,
// don't have each bill set already. Instead, use BillsCalculator's
// initialiser and pass in bills.
let bill1 = Bill(name: "Medical", amount: 34.25)
let bill2 = Bill(name: "Hulu", amount: 7.99)
let cal = BillsCalculator(bill1: bill1, bill2: bill2)
print("You've spend \(cal.totalBilled) this month")