Replacing variables names dynamically like string parts - arrays

I'd like to optimise my code using a single function to process multiple possibilities depending on the situation: here it is different currencies.
I know how to do it when I use strings with the \(something) syntax and I'd like something similar for variables.
Here is my current situation for USD:
if WalletViewController.currencyUSD == true {
if CurrentDifferenceFunctions.buyOrSellBitcoinUSD == 1 {
if let lastBitcoinBuyPrice = UserDefaults.standard.object(forKey: "lastBitcoinBuyPrice\(currentCurrency)") as? Double {
CurrentDifferenceFunctions.savedBitcoinPrice = lastBitcoinBuyPrice
}
} else if CurrentDifferenceFunctions.buyOrSellBitcoinUSD == 2 {
if let lastBitcoinSellPrice = UserDefaults.standard.object(forKey: "lastBitcoinSellPrice\(currentCurrency)") as? Double {
CurrentDifferenceFunctions.savedBitcoinPrice = lastBitcoinSellPrice
}
}
}
How can I replace USD everytime it appears by another currency when needed and then be able to use the same function for different currencies? Would using arrays help here?
I have a few dozens cryptos and as many currencies to process, any help would be gold!
PS: I'm sorry about the redundant WalletViewController.currencyUSD == true but I like to keep my work literal until I fully understand the logic.

Related

Randomly grab string from array, use as placeholder text, and equate to answer string in swift

I have two UITextFields that get the placeHolder.text randomly from an array. The part I am stuck on, is having an element in an array of answers, correspond to the placeHolder text.
Meaning: placeHolder.text is a question, user text input is the answer, and we check that the user answer is the same as the hard coded value.
sample code:
let questionArray = ["name of your cat?", "name of your other cat?"]
let questionArray2 = { more stupid questions ]
let answerArray = ["cat damen", "diabetes"]
let answerArray2 = [ more stupid answers ]
var answerContainer: String()
var answerContainer2: String()
uitextField1.placeHolder = questionArray.randomElement()
uitextField2.placeHolder = questionArray2.randomElement()
Heres where I get stupid :
func answerGenerator {
if UItextField1.placeHolder == questionArray[0] {
answerContainer = answerArray[0]
}
}
func submit(_ sender: UIButton) {
if uitextField.text == answerContainer && uitextField2.text == answerContainer2 {
do good stuff
}
}
I tried using a dictionary, but got stuck. I also thought about an Enum / Switch Case, but also got stuck as I have two textFields that need the placeHolder generated randomly.
so what happens is the actual answer that's supposed to be allocated to its question, maybe it happens, but I'm not grabbing it correctly to compare what the user will type in the textfield.
Its so easy, but I'm overthinking this.

Splitting string into array string.components(separtedBy: ",") consumes more time

I have text file which contains 18000 lines which have cities names. Each line has city name, state, latitude, longitude etc. Below is the function which does that, if i don't implement string.components(separtedBy: ", ") loading function is pretty fast but with it implemented it takes time which makes my UI freeze. What is the right way of doing it? Is string.components(separtedBy: ", ") that costly?
I profiled the app, this line is taking string.components(separtedBy: ", ") 1.45s out of 2.09s in whole function.
func readCitiesFromCountry(country: String) -> [String] {
var cityArray: [String] = []
var flag = true
var returnedCitiesList: [String] = []
if let path = Bundle.main.path(forResource: country, ofType: "txt") {
guard let streamReader = StreamReader(path: path) else {fatalError()}
defer {
streamReader.close()
}
while flag {
if let nextLine = streamReader.nextLine() {
cityArray = nextLine.components(separatedBy: ",") // this is the line taking a lot of time, without this function runs pretty fast
if (country == "USA") {
returnedCitiesList.append("\(cityArray[0]) , \(cityArray[1]) , \(cityArray[2])")
} else {
returnedCitiesList.append("\(cityArray[0]) , \(cityArray[1])")
}
//returnedCitiesList.append(nextLine)
} else {
flag = false
}
}
} else {
fatalError()
}
return returnedCitiesList
}
StreamReader used in the code can be found here. It helps to read file line by line
Read a file/URL line-by-line in Swift
This question is not about how to split the string into array Split a String into an array in Swift? , rather why splitting is taking more time in the given function.
NSString.components(separatedBy:) returns a [String], which requires that all of the pieces' content be copied, from the original string, and pasted into new-ly allocated stringss. This slows things down.
You could address the symptoms (UI freezing) by putting this work on a background thread, but that just sweeps the problem under the wrong (the inefficient copying is still there), and complicates things (async code is never fun).
Instead, you should consider using String.split(separator:maxSplits:omittingEmptySubsequences:), which returns [Substring]. Each Substring is just a view into the original string's memory, which stores the relevant range so that you only see that portion of the String which is modeled by the Substring. The only memory allocation happening here is for the array.
Hopefully that should be enough to speed your code up to acceptable levels. If not, you should combine both solutions, and use split off-thread.

How do I move String values from an array to a tuple without copying?

I have a fixed size array of Strings: [String; 2]. I want to turn it into a (String, String). Can I do this without copying the values?
The piece of code that I'm working on in particular is the following:
let (basis, names_0, names_1) = if let Some(names) = self.arg_name {
(ComparisonBasis::Name, names[0], names[1])
} else {
(ComparisonBasis::File, self.arg_file[0], self.arg_file[1])
};
types:
self.arg_name: Option<[String; 2]>
self.arg_file: Vec<String>
Right now I'm getting errors
cannot move out of type `[std::string::String; 2]`, a non-copy fixed-size array [E0508]
and
cannot move out of indexed content [E0507]
for the two arms of the if
You've omitted a fair amount of context, so I'm taking a guess at a few aspects. I'm also hewing a little closer to the question you asked, rather than the vaguer one implied by your snippets.
struct NeverSpecified {
arg_names: Option<[String; 2]>,
arg_file: Vec<String>,
}
impl NeverSpecified {
fn some_method_i_guess(mut self) -> (String, String) {
if let Some(mut names) = self.arg_names {
use std::mem::replace;
let name_0 = replace(&mut names[0], String::new());
let name_1 = replace(&mut names[1], String::new());
(name_0, name_1)
} else {
let mut names = self.arg_file.drain(0..2);
let name_0 = names.next().expect("expected 2 names, got 0");
let name_1 = names.next().expect("expected 2 names, got 1");
(name_0, name_1)
}
}
}
I use std::mem::replace to switch the contents of the array, whilst leaving it in a valid state. This is necessary because Rust won't allow you to have a "partially valid" array. There are no copies or allocations involved in this path.
In the other path, we have to pull elements out of the vector by hand. Again, you can't just move values out of a container via indexing (this is actually a limitation of indexing overall). Instead, I use Vec::drain to essentially chop the first two elements out of the vector, then extract them from the resulting iterator. To be clear: this path doesn't involve any copies or allocations, either.
As an aside, those expect methods shouldn't ever be triggered (since drain does bounds checking), but better paranoid than sorry; if you want to replace them with unwrap() calls instead, that should be fine..
Since Rust 1.36, you can use slice patterns to bind to all the values of the array at once:
struct NeverSpecified {
arg_names: Option<[String; 2]>,
arg_file: Vec<String>,
}
impl NeverSpecified {
fn some_method_i_guess(mut self) -> (String, String) {
if let Some([name_0, name_1]) = self.arg_names.take() {
(name_0, name_1)
} else {
let mut names = self.arg_file.drain(0..2);
let name_0 = names.next().expect("expected 2 names, got 0");
let name_1 = names.next().expect("expected 2 names, got 1");
(name_0, name_1)
}
}
}
See also:
Method for safely moving all elements out of a generic array into a tuple with minimal overhead

Swift UISlider that gets its values from an array of numbers

I'm a newbie to swift and believe me I've searched and searched for an answer already. I want to create UISliders that get their values from an array of numbers in Swift. Its a camera app so the example array should be obvious.
#IBAction func isoValueChanged(sender: UISlider) {
let isoArray = ["24", "32", "50","64","80","100","125","160","200","250","320","400","500","640","720","800","1000","1250","1600","1800"] // available iPhone 6s ISO settings I believe
let currentValue = // What do I need?
isoText.text = "\(currentValue)"
}
Even harder would be representing shutter speeds from 1/1000 - 32!
From what I see out there this is not an easy one because there is no mathematical representation to calc from the array. Is it even possible?
I'm not quite sure I understand what you want this for but I'm guessing this is right.
// Global Variable or something like this (accessible from multiple functions)
let isoArray = ["24", "32", "50","64","80","100","125","160","200","250","320","400","500","640","720","800","1000","1250","1600","1800"] // available iPhone 6s ISO settings I believe
func functionThatCreatesTheSliderYo(...) {
slider.minimumValue = 0
slider.maximumValue = isoArray.count
slider.continuous = false
}
#IBAction func isoValueChanged(sender: UISlider) {
// instead of Int(val) you may want to round(val) for a better UI
let currentValue = isoArray[Int(sender.value)]
isoText.text = "\(currentValue)"
}
This works for me
#IBAction func isoValueChanged(sender: UISlider) {
let isoArray = ["24", "32", "50","64","80","100","125","160","200","250","320","400","500","640","720","800","1000","1250","1600","1800"]
let currentValue = isoArray[Int(sender.value)]
isoText.text = "\(currentValue)"
}
So simple as it should be in Swift. Many thanks to Ty. Be sure to see the chat though.

Having array problems in Swift

I am learning how to build apps and working with Swift for this project.
I had a buddy help me pull data in from a website and it looks like he created classes with variables and mapped them to certain extensions (IE "Username") so when I call the variable data such as profile I would call it. The below uses luck_30 able to store "Stats.luck_30"
luck_30.text = profile.luck_30
So inside one of my variables that is in this "Profile" class is setup into an array. I can pull the array out of the class, but I can't seem to do for while statement replacing the [#] with a variable from the for command.
func aliveWorkers(profile: Profile) -> NSNumber{
var myworkers : Array = profile.workers!
//this test works and returns the proper value
var testworker: NSNumber = myworkers[0].alive!
println("The satus of the test worker is " + testworker.description)
/* This code is giving error "Could not find member alive" it does not ifor var
for ifor in myworkers{
var thisworker: NSNumber = myworkers[ifor].alive! as NSNumber
}
*/
return 42
}
Your variable ifor is not a counter, it is an actual object. You could do something like this:
for worker in myWorkers {
let workerIsAlive = worker.alive!
}
Alternatively, if you need the index,
for i in 0 ..< myWorkers.count {
let worker = myWorkers[i]
let workerIsAlive = worker.alive!
}
If you need both:
for (i, worker) in enumerate(myWorkers) {
let workerIsAlive = worker.alive!
}
And as a matter of style, I would stay away from NSNumber and use Int or Bool or whatever the data actually is. Also, it looks like the alive variable should not be optional, as you're unwrapping it everywhere. To avoid "mysterious" crashes later, you may want to think about making it a non-optional type.
when using a for in loop, your loop variable isn't an index, its the objects you're looping through. so..
func aliveWorkers() {
var myworkers = [1, 2, 3]
//this test works and returns the proper value
let testworker = myworkers[0]
print("The satus of the test worker is \(testworker)")
for ifor in myworkers {
print(ifor)
}
}
Notice a few things... you don't need to use + to concatenate those strings. you can just use string interpolation. \(variable) inserts the value of variable in the string.
Try to use let instead of var when you don't change the variable. You don't need to explicitly define type on variables either.

Resources