Showing only two decimal numbers stored in array - arrays

I have an array var priceUSDcoin = [String]() that contain many numbers comes from JSON data that have more than 2 decimal numbers like 2.073334 and i want all the numbers in the array have only 2 decimal numbers like 2.07
I tried to use String(format: "%.2f") and failed
I called the priceUSDcoin here cell.priceLable.text = "$" + priceUSDcoin[indexPath.row] in my tableview

You can use a NumberFormatter to get the desired results. Using a single flatMap operation, you can convert all elements of your array to the desired format after converting the String prices to Double.
var priceUSDcoin = [String]()
priceUSDcoin = ["2.073334","1.245","0.1123"]
let priceFormatter = NumberFormatter()
priceFormatter.maximumFractionDigits = 2
priceFormatter.minimumIntegerDigits = 1
let priceUSD = priceUSDcoin.flatMap{ priceString->String? in
guard let price = Double(priceString) else {return nil}
return priceFormatter.string(for: price)
}
print(priceUSD)
Output:
["2.07", "1.24", "0.11"]
If you want to display the prices to the user, you can even set priceFormatter.numberStyle = .currency, in which case the results will be formatted as currency using the user's Locale settings. Since your prices are all in USD, you can hardcode the Locale using priceFormatter.locale = Locale(identifier: "en_US_POSIX").
Output:
["$2.07", "$1.24", "$0.11"]

I have an array var priceUSDcoin = [String]() that contain many numbers comes from JSON
You shouldn't convert numbers from JSON to String type. Try to save them to Double type. In this case your code (with little update) works fine:
let priceUSDcoin = [2.073334] // Array of `Double`
print(String(format: "%.2f", priceUSDcoin[0])) // prints 2.07

Related

How do I calculate the sum of the respective variables of a custom type array?

I am new to coding in Swift. My goal is to build a variable containing the sum of a number of custom class variables.
To speak in clear terms:
I have a custom class called "Entry" which has the Double variable "sum", e.g. 50.00.
I am using a ViewController to let the user create a new entry with text field inputs, inter alia a sum of the new entry. This new entry element is then appended to an array of type Entry when the relevant button is pressed.
#IBAction func addEntry(_ sender: UIButton){
// take user input
let name = nameTextField.text ?? ""
let sum = Double(sumTextField.text!) ?? 0.0
let category = selectedCategory
// save user input in new entry
let newEntry = Entry(name: name, sum: sum, category: category)
entries.append(newEntry)
saveEntries()
os_log("Saved new entry successfully.", log: OSLog.default, type: .debug)
In another ViewController, I want to access the array "entries" and build the sum of all sum variables of all Entry elements, e.g. sum of Entry 1 + sum of Entry 2 + sum of Entry 3
My current attempt in coding is as follows:
var entriesDatabase = [Entry]()
//extract sum of entries and build sumOfEntries
var sumArray = [Double]()
for entry in entriesDatabase {
sumArray.append(entry.sum)
}
sumOfEntries = sumArray.reduce(0, +)
The entries array from the first View Controller is saved by using the NSKeyedArchiver and loaded by NSKeyedUnarchiver.unarchiveObject(withFile:) in the second View Controller before calling the function above (which, I am aware, has been deprecated, but it works for my current purposes).
I used the print function to isolate the issue and as far as I can see, the sumOfEntries always remains 0.0, no matter how many Entry elements I create (which itself seems to work, though).
Has anyone a clue what I am doing wrong?
EDIT: The issue to me really seems to be that the calculation does not work rather than the passing of the data from one view to another. Somehow, the arrays always remain empty. The passage of the data works via saving it persistently on the drive and then loading it with the NSKeyedArchiver functions. For clarity see the following code:
/MARK: calculate and display balance
func calculate(){
//load data from user defaults
recurringCalculationValue = UserDefaults.standard.value(forKey: "recurringExpenses") ?? 0.0
monthlyIncomeCalculationValue = UserDefaults.standard.value(forKey: "monthlyIncome") ?? 0.0
//extract sum of entries and build sumOfEntries
var sumArray = [Double]()
for entry in entriesDatabase {
sumArray.append(entry.sum)
}
sumOfEntries = sumArray.reduce(0, +)
//cast the user defaults into Double
let mICV = monthlyIncomeCalculationValue as! Double
let rCV = recurringCalculationValue as! Double
//convert the Strings to Double! and calculate balance
balance = Double(mICV) - Double(rCV) - sumOfEntries
//display balance in sumLabel
sumLabel.text = "\(balance)"
//debugging
print("balance = \(balance)")
print("sumOfEntries = \(sumOfEntries)")
print("monthlyIncomeCalculationValue = \(monthlyIncomeCalculationValue)")
print("recurringCalculationValue = \(recurringCalculationValue)")
print("UserDefault monthlyIncome = \(String(describing: UserDefaults.standard.value(forKey: "monthlyIncome")))")
print("UserDefault recurringExpenses = \(String(describing: UserDefaults.standard.value(forKey: "recurringExpenses")))")
}
//this function is called when the ViewController is opened
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
//Load saved entries into entries array if entries were saved
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
if let pathComponent = url.appendingPathComponent("entries") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("File available.")
entriesDatabase = loadEntries()!
print("Database loaded.")
} else {
print("File not available.")
}
} else {
print("File path not available.")
}
calculate()
}
private func loadEntries() -> [Entry]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: Entry.ArchiveURL.path) as? [Entry]
}
I hope, that makes my problem clearer and thanks again!
The way I see it, in var entriesDatabase = [Entry]() you create a new array for objects of type Entry, which is (of course) initially empty. Therefore, the sum of the values will be 0.
What you want is to cache the values, e.g. in your saveEntries()-Function. You may want to take a look at UserDefaults which stores information in a map-like matter.

How to Multiply Each element of an array multiply by each element of Next array in Swift?

I had two Arrays.
let quntityArr = ["1","3","4","7"]
let priceArr = ["£129.95", "£179.95","£169.95","£199.85"]
I want to multiply these both Arrays in the following way
let totalArr = ["1*£129.95", "3*£179.95", "4*£169.95", "7*£199.85"]
Here I want to calculate each price with those product quantities.
You need
let quntityArr:[Double] = [1,3,4,7]
let priceArr = [129.95, 179.95,169.95,199.85]
let totalArr = zip(quntityArr, priceArr).map { "£\($0 * $1)" }
print(totalArr)
Assuming your input data is provided as array of String.
1. Input Data
let quntityArr = ["1","3","4","7"]
let priceArr = ["£129.95", "£179.95","£169.95","£199.85"]
2. Convert input data in array of Int and Double
let quantities = quntityArr
.compactMap(Int.init)
let prices = priceArr
.map { $0.dropFirst() }
.compactMap (Double.init)
3. Verify no input value has been discarded
assert(quntityArr.count == quantities.count)
assert(priceArr.count == prices.count)
4. Do the math
let results = zip(quantities, prices).map { Double($0) * $1 }.map { "£\($0)"}
5. Result
["£129.95", "£539.8499999999999", "£679.8", "£1398.95"]

Sorting a dictionary by key and converting it into an array

I have a dictionary of prices and quantities. I am getting updates on the price and values multiple times in a second so I don't want to store them in an array because dictionary are much faster.
let mainPriceValDict = [Double:Double]()
The data is coming in as an array of JSON so I am using codable to parse the JSON and put it into a dictionary. When I use the data, it needs to be sorted in ascending and/or descending order because I am looping through each price in order to get to a certain total quantity. The format that the array is in that I am looping through is as follows:
let loopingArray = [PriceQuantityEntry]()
struct PriceQuantityEntry {
let price : Double
let size : Double
}
I want to sort the prices which are the keys in the dictionary above and convert them into an array of PriceQuantityEntry. What is the best way to do this? In ascending and deciding order. I have tried first getting all the keys sorted and then grabbing associated values and putting them into the array in order but this seems like more processing than this task actually requires.
I think the best way to do this would be to put a custom initializer in the struct to convert the dictionary value to a value of type PriceQuantityEntry but I am not exactly sure how that would work with the sorting.
This is what I am currently doing to get it to work. I just feel like there is a more efficient way for it to be done. If you feel like I should keep the structure as an array instead of converting it to a dict, let me know.
loopingArray = self.mainPriceValDict.sorted { $0.0 < $1.0 }.map { PriceQuantityEntry(price: $0.0, size: $0.1) }
If you are getting a lot of updates to individual entries, both a dictionary and an array may cause memory copies of the whole memory structure every time an entry is changed.
I would suggest using objects (classes) instead of structures. This will allow you to use both an array and a dictionary to reference the object instances. The dictionary will provide direct access for updates and the array will allow sequential processing in forward or backward order.
[EDIT] Example:
class PriceQuantityEntry
{
static var all:[PriceQuantityEntry] = []
static var prices:[Double:PriceQuantityEntry] = [:]
var price : Double
var size : Double
init(price:Double, size:Double)
{
self.price = price
self.size = size
PriceQuantityEntry.all.append(self)
// PriceQuantityEntry.all.resort() // on demand or when new prices added
PriceQuantityEntry.prices[price] = self
}
class func update(price:Double, with size:Double)
{
if let instance = PriceQuantityEntry.prices[price]
{ instance.size = size }
else
{
let _ = PriceQuantityEntry(price:price, size:size)
PriceQuantityEntry.resort()
}
}
class func resort()
{
PriceQuantityEntry.all.sort{$0.price < $1.price}
}
}
// if adding multiple initial entries before updates ...
let _ = PriceQuantityEntry(price:1, size:3)
let _ = PriceQuantityEntry(price:1.25, size:2)
let _ = PriceQuantityEntry(price:0.95, size:1)
PriceQuantityEntry.resort()
// for updates ...
PriceQuantityEntry.update(price:1, with: 2)
// going throug list ...
var count:Double = 0
var total:Double = 0
var quantity:Double = 5
for entry in PriceQuantityEntry.all
{
total += min(entry.size,quantity-count) * entry.price
count = min(quantity,count + entry.size)
if count == quantity {break}
}

Creating multi-dimensional arrays containing different types in Swift

I want to create a multi-dimensional array in Swift and following on from this post on creating multi-dimensional arrays, I came up with:
var itemList: [[(String, Date, Double, Int)]] = []
let itemID = purchases["itemID"] as! String
let purchase_date = purchases["purchase_date"] as! Date
let purchase_date_ms = purchases["purchase_date_ms"] as! Double
let quantity = purchases["quantity"] as! Int
itemList.append([(itemID, purchase_date, purchase_date_ms, quantity)])
This all seems ok but when I try to get the data back out with:
var subPurchaseDate: Date
subPurchaseDate = itemList[0][1]
to try to read the "purchase_date" value from the array I get the error "Cannot assign value of type '(String, Date, Double, Int)' to type 'Date'", and
switch itemList[iloop][0] {
...
}
gives "Expression pattern of type 'String' cannot match values of type '(String, Date, Double, Int)'"
Any clues as to why it's not taking the value/type of the element that I'm trying to specify in <array>[i][j] but seems to be trying to take <array>[i]? What am I not seeing?
You have stored tuple value inside array. so, access the contents of a tuple by providing the index position .
subPurchaseDate = itemList[0][1].0 // for string value
subPurchaseDate = itemList[0][1].1 // for date
subPurchaseDate = itemList[0][1].2 // for Double
subPurchaseDate = itemList[0][1].3 // for Int
You can also access using it's named value.
var itemList: [[(stringValue: String, dateVal: Date, doubleValue: Double, intValue: Int)]] = []
// append elements in `itemList`
itemList.append([(stringValue: itemID, dateVal: purchase_date, doubleValue: purchase_date_ms, intValue: quantity)])
// access using it's named value
subPurchaseDate = itemList[0][1].stringValue // for string value
subPurchaseDate = itemList[0][1].dateVal // for date
subPurchaseDate = itemList[0][1].doubleValue // for Double
subPurchaseDate = itemList[0][1].intValue // for Int

How to return an subset of Array of characters?

I am trying to dynamically chop an array of letters but I can't seem to reconvert the result back into a [String]
let letters:String = "abcdefghijklmnopqrstuvwxyz"
let lettersarray = Array(letters.characters)
var targetNum = 14 // Show just the first 14 characters
var resultsArray = [String]()
let resultsSlice = lettersarray.dropLast(lettersarray.count - targetNum) // Returns an Array Slice instead of an Array
let newresultsArray = Array(resultsSlice) // Returns Array<_element> instead of [String]
How do I return a [String] ie ["a","b","c"... eg]
You need to map the Character array back to String
let resultsArray = lettersarray.dropLast(lettersarray.count - targetNum).map{String($0)}
alternatively (credits to Leo Dabus)
let letters = "abcdefghijklmnopqrstuvwxyz"
let targetNum = 14
let resultsArray = letters.characters.prefix(targetNum).map{String($0)}
No need for an array here. It's hard to understand what you're trying to do, but if you just want the first 14 characters of a string, use the prefix method:
let s = String("abcdefghijklmno".characters.prefix(14))
Assuming that you are getting a String and want an array of characters which is a slice of characters from that String, you could use Swift's Half-Open Range Operator found here.
let letters:String = "abcdefghijklmnopqrstuvwxyz"
let lettersArray = Array(letters.characters)
let targetNum = 2
let resultsArray = lettersArray[0..<targetNum]
This will give you an ArraySlice<Character>. If you want an Array<Character> you could do this:
let resultsArray:Array<Character> = Array(lettersArray[0..<targetNum]) // ["a","b"]

Resources