Create an array function within a class in Swift - arrays

I have spent hours trying to find a solution but can't find any. Apologies - novice here.
I want to add addCrewMember function in RocketShip class that will allow me to add new members to the crewMembers array.
I know that I should use append method but it keeps giving me error. Thoughts?
class RocketShip
{
var speed: Double
let modelNumber: Int
let shipName : String
var crewMembers: Array<String>
init (name: String, number: Int)
{
shipName = name
modelNumber = number
speed = 0.0
crewMembers = ["John", "Jane"]
}
func addSpeed(addedSpeed: Double)-> Double
{
speed = speed + addedSpeed
return speed
}
func addBoostedSpeed(addedSpeed: Double, numberOfBoosts: Int) -> Double
{
let boostedSpeed = addedSpeed * Double (numberOfBoosts)
speed = speed + boostedSpeed
return speed
}
func addCrewMembers(addCrewMembers: String) -> STring
{
addCrewMembers = crewMembers.append()
return crewMembers
}
}

You probably mean
func addCrewMembers(newMember: String) -> [String]
{
crewMembers.append(newMember)
return crewMembers
}

Related

Combine / Merge Multiple Arrays Into One

I have four list of arrays:
var products: [Product] = []
var addressInfo: [AddressInfo] = []
var favorites: [Favorite] = []
var amounts: [Amount] = []
I want to combine them all into one so I have only one variable:
var combined = [Product/AddressInfo/Favorite/Amount]()
How would I go about merging them into one array var cobmbined = [Combined]() ?
This these are two examples of how my structs look like:
struct Amount {
var amountsDeliveryCharge: Double
var amountsDriverTip: Double
init(
amountsDeliveryCharge: Double,
amountsDriverTip: Double
){
self.amountsDeliveryCharge = amountsDeliveryCharge
self.amountsDriverTip = amountsDriverTip
}
init(data: [String: Any]){
amountsDeliveryCharge = data[DatabaseRef.deliveryCharge] as? Double ?? 0.0
amountsDriverTip = data[DatabaseRef.driverTip] as? Double ?? 0.0
}
static func modelToData(amount: Amount) -> [String: Any] {
let data : [String: Any] = [
DatabaseRef.deliveryCharge : amount.amountsDeliveryCharge,
DatabaseRef.driverTip : amount.amountsDriverTip
]
return data
}
}
This is the other struct:
struct Product {
var price: Double
var priceUnit: String
init(
price: Double,
priceUnit: String
){
self.price = price
self.priceUnit = priceUnit
}
init(data: [String: Any]){
price = data[DatabaseRef.price] as? Double ?? 0.0
priceUnit = data[DatabaseRef.priceUnit] as? String ?? ""
}
static func modelToData(product: Product) -> [String: Any] {
let data : [String: Any] = [
DatabaseRef.price : product.price,
DatabaseRef.priceUnit : product.priceUnit
]
return data
}
}
The other two structs look similar in structure as well.
Assuming the ordering is the same in each of them, create a struct with the appropriate properties
struct Combined {
let product: Product
let addressInfo: AddressInfo
let favourite: Favourite
let amount: Amount
}
and then either loop through the arrays using a counter to index then and create instances using the memberWise initialiser, or use a static factory function to make and return the array:
static func makeFrom(products: [Product], addresses: [AddressInfo], favourites: [Favourite], amounts: [Amount]) -> [Combined] {
guard products.count == addresses.count, addresses.count == favourites.count, favourites.count == amounts.count else {fatalError()}. //handle better in practice
var combined = [Combined]()
for index in 0..<products.count {
combined.append(Combined(product: products[index],
addressInfo: addresses[index],
favourite: favourites[index],
amount: amounts[index])
}
return combined
}
The above checks that all arrays are teh same length and if not calls fatalError(). In reality you'd want to handle the error better than this.
Then use as:
let combined = Combined.makeFrom(products: products, addresses:addressInfo, favourites: favourites, amounts: amounts)
You can cast your arrays as [Any] and add them.
let combinedVals = (products as [Any]) + (addressInfo as [Any]) + (favorites as [Any]) + (amounts as [Any])
Alternatively you can create a protocol and have your structs adhere to it.
protocol Combined {
}
struct Amount: Combined {
var amountsDeliveryCharge: Double
var amountsDriverTip: Double
}
struct Product: Combined {
var price: Double
var priceUnit: String
}
let combinedValues: [Combined] = amounts + products
When you iterate over the array, if you need to check what it is, you can use a switch to check it's type.
combinedValues.forEach { (value) in
switch value {
case let value as Amount:
print(value.amountsDriverTip)
break
case let value as Product:
print(value.price)
break
default:
break
}
}

Hash Tables: Ransom Note - Hacker Rank in Swift Timed Out

My code is alright but keeping getting time out on some test cases, any tips to improve this? My guess is the indexOf function taking too long.
func checkMagazine(magazine: [String], note: [String]) -> Void {
var mutableMag = magazine
if note.count > mutableMag.count {
print("No")
return
}
for word in note {
if let index = mutableMag.index(of: word) {
mutableMag.remove(at: index)
} else {
print("No")
return
}
}
print("Yes") }
Please find the challenge in this link: https://www.hackerrank.com/challenges/ctci-ransom-note/problem
One possible solution that passes all tests is using NSCountedSet for storing the words in the note and magazine and comparing the count of each word in note to the count of that word in magazine and if any of them is lower in magazine, making an early return and printing No.
I'd also suggest changing the function signature to return a Bool value even though the function prototype generated by hacker rank returns Void. It's better to make checkMagazine a pure function and not doing any I/O operations in it.
func checkMagazine(magazine: [String], note: [String]) -> Bool {
let magazineWords = NSCountedSet(array: magazine)
let noteWords = NSCountedSet(array: note)
for noteWord in noteWords {
if magazineWords.count(for: noteWord) < noteWords.count(for: noteWord) {
return false
}
}
return true
}
Then you just need to change the end of the generated code to the following:
let magazineWorks = checkMagazine(magazine: magazine, note: note)
if magazineWorks {
print("Yes")
} else {
print("No")
}
func checkMagazine(magazine: [String], note: [String]) -> Void {
var notesDictionary : [String : Int] = [:]
var magazineDictionary : [String : Int] = [:]
var canMakeRansom = true
for n in note {
var count = notesDictionary[n] ?? 0
count += 1
notesDictionary[n] = count
}
for n in magazine {
var count = magazineDictionary[n] ?? 0
count += 1
magazineDictionary[n] = count
}
for note in notesDictionary {
if note.value > magazineDictionary[note.key] ?? 0 {
canMakeRansom = false
}
}
print(canMakeRansom ? "Yes" : "No")
}
This is another way to solve this.
I think this does what NSCountedSet does by itself somehow.

convert array of string into Double in swift

I'm trying to convert a string into a double in swift. I managed to extract the string from a website (www.x-rates.com) into an array but I cannot convert it after in a double in order to make some work around this number. Can anyone tell me what I'm supposed to do or what I did wrong? I know that my label don't update now but I will do it later, the first thing that I'm trying to do is the conversion.
thx a lot!
Here is the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var moneyTextField: UITextField!
#IBAction func convert(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var message = ""
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
message = newContentArray[0]
var message = Float(newContentArray[0])! + 10
}
}
}
}
}
DispatchQueue.main.sync(execute: {
self.resultLabel.text = "the value of the dollar is " + message
}
)}
task.resume()
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I will talk about convert an Array of String to Array of Double.
In swift Array has a method called map, this is responsable to map the value from array, example, in map function you will receive an object referent to your array, this will convert this object to your new array ex.
let arrOfStrings = ["0.3", "0.4", "0.6"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double in
return Double(value)!
}
The result will be
UPDATE:
#LeoDabus comments an important tip, this example is considering an perfect datasource, but if you have a dynamic source you can put ? on return and it will work, but this will return an array with nil
like that
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double? in
return Double(value)
}
Look this, the return array has a nil element
If you use the tips from #LeoDabus you will protect this case, but you need understand what do you need in your problem to choose the better option between map or compactMap
example with compactMap
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.compactMap { (value) -> Double? in
return Double(value)
}
look the result
UPDATE:
After talk with the author (#davidandersson) of issue, this solution with map ou contactMap isn't his problem, I did a modification in his code and work nice.
first I replaced var message = "" per var rateValue:Double = 0.0 and replacedFloattoDouble`
look the final code
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var rateValue:Double = 0.0;
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
rateValue = Double(newContentArray[0])! + 10
}
}
}
}
}
//
print("Rate is \(rateValue)"); //Rate is 11.167
}
task.resume()
Hope to help you
The reason your code doesn’t work in my opinion is that you have two variables with the same name that are defined in different scopes and you use the wrong one at the end.
At the beginning you define
var message = ""
And then when converting to a number further down
var message = Float(newContentArray[0])! + 10
So change the last line to something like
var number = Float(newContentArray[0])! + 10
And use number in your calculations. Although I think
var number = Double(message)
should work equally fine since you have assigned newContentArray[0] to message already and Double is more commonly used than Float (I don’t understand + 10)

How to encode an array of CGPoints with NSCoder in Swift?

I am trying to save a copy of my custom class to a file, my class has 2 arrays of CGPoints which I append to every so often, they look like this:
class BlockAttributes: NSObject {
var positions:[CGPoint] = []
var spawns:[CGPoint] = []
}
Everything is working great as far as just as using and accessing the class goes, but archiving it does not work. I can archive arrays of Strings, Bools, and Ints just fine in my other classes but my game fails every time I try to use NSCoder to encode my arrays of CGPoints. Here is my code for archiving:
func encodeWithCoder(coder: NSCoder!) {
coder.encodeObject(positions, forKey: "positions")
coder.encodeObject(spawns, forKey: "spawns")
}
....
class ArchiveData: NSObject {
var documentDirectories:NSArray = []
var documentDirectory:String = ""
var path:String = ""
func saveData(data: BlockAttributes) {
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if NSKeyedArchiver.archiveRootObject(data, toFile: path) {
print("Success writing to file!")
} else {
print("Unable to write to file!")
}
}
func retrieveData() -> NSObject {
var dataToRetrieve = BlockAttributes()
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as! String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? BlockAttributes {
dataToRetrieve = dataToRetrieve2 as BlockAttributes
}
return(dataToRetrieve)
}
}
....
And to save:
let archiveData = ArchiveData()
archiveData.saveData(myBlockActionsObject)
I even tried creating my own custom class to save the CGPoints to, which I call MyCGPoint (I read somewhere on SO that creating custom classes for some data types resolves some NSCoder issues):
class MyCGPoint: NSObject {
var x: CGFloat = 0.0
var y: CGFloat = 0.0
init(X: CGFloat, Y: CGFloat) {
x = X
y = Y
}
override init() {
}
}
....
class BlockAttributes: NSObject {
var positions:[MyCGPoint] = []
var spawns:[MyCGPoint] = []
}
But alas, I am still getting this error:
[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0 Game[20953:5814436]
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[Game.MyCGPoint encodeWithCoder:]:
unrecognized selector sent to instance 0x137f1d1a0'
Any idea how I can use encodeObject to encode my array of CGPoints/MyCGPoints?
You can convert them to and from strings:
//To string
let point = CGPointMake(0, 0)
let string = NSStringFromCGPoint(point)
//Or if you want String instead of NSString
let string = String(point)
//From string
let point2 = CGPointFromString(string)
CGPoint (and its Cocoa's twin NSPoint) are structs, i.e. value type, so you can't encode them directly. Wrap them in NSValue:
let positionValues = positions.map { NSValue(point:$0) }
let spawnValues = spawns.map { NSValue(point:$0) }
coder.encodeObject(positionValues, forKey: "positions")
coder.encodeObject(spawnValues, forKey: "spawns")
// Decode:
positons = (coder.decodeObjectForKey("positions") as! [NSValue]).map { $0.pointValue }
spawns = (coder.decodeObjectForKey("spawns") as! [NSValue]).map { $0.pointValue }
When you write your custom wrapper class, you have to make it compliant with NSCoding too, which NSValeu had already done for you, for free.

Swift 1.2 Filter an Array of Structs by keyword

I need some help filtering an array of Structs.
This is what I am doing currently, it filters the array but not correctly.
For example lets say I search for an item in the array with "Mid" I have one item that should be shown however the item shown starts with "Bad".
var array = breweries.filter() { $0.name?.lowercaseString.rangeOfString(searchController.searchBar.text.lowercaseString) != nil }
results = array
here is my Struct
struct Breweries {
let name: String?
let breweryId: String?
let distance: Double?
let largeIconURL: String?
let streetAddress: String?
let locality: String?
let region: String?
let phone: String?
let website: String?
init(brewDictionary: [String: AnyObject]) {
name = brewDictionary["brewery"]?["name"] as? String
breweryId = brewDictionary["breweryId"] as? String
distance = brewDictionary["distance"] as? Double
largeIconURL = brewDictionary["brewery"]?["images"]??.objectForKey("large") as? String
streetAddress = brewDictionary["streetAddress"] as? String
locality = brewDictionary["locality"] as? String
region = brewDictionary["region"] as? String
phone = brewDictionary["phone"] as? String
website = brewDictionary["website"] as? String
}
}
Please point in the right direction!
Note: I am using Swift 1.2
Update:
I thought a video would be of help to better explain what I am trying to do.
Demo Of issue
What I want is to find the filter the array so only the item with a similar name is shown.
Update 2: As it turns out I forgot to handle the case when my UISearchController was active.
Assuming your Struct name is Breweries and it has a name property, try this:
let array = breweries.filter() {
($0.name!.lowercaseString as NSString).containsString(searchController.searchBar.text.lowercaseString)
}
Your usage of filter is correct, but your closure seem to be complicated with no clear goal. I suggest you to write an extension (or possibly use what I am using):
extension String {
func contains(search: String, ignoreCase: Bool = false, ignoreDiacritic: Bool = false) -> Bool {
var options = NSStringCompareOptions.allZeros
if ignoreCase { options |= NSStringCompareOptions.CaseInsensitiveSearch }
if ignoreDiacritic { options |= NSStringCompareOptions.DiacriticInsensitiveSearch }
return self.rangeOfString(search, options: options) != nil
}
}
This way you can use closure like this to search:
breweries.filter() {
$0.name?.contains("x") // Precise search
$0.name?.contains("x", ignoreCase: true, ignoreDiacritics: true) // Ignores diacritics and lower / upper case
}
of course, you can use | or & to search for multiple parameters
breweries.filter() {
$0.name?.contains("x") || $0.streetAddress?.contains("x")
}
Hope it helps!
Here is an example from an investing app with struct:
import Foundation
public struct SNStock {
public let ticker:NSString
public let name:NSString
init(ticker:NSString, name:NSString) {
self.ticker = ticker
self.name = name
}
}
Search on Main Thread:
public func searchStocksByKeyword(keyword:String) -> [SNStock] {
let lowercaseKeyword = keyword.lowercaseString
var searchResults:[SNStock] = []
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.ticker.lowercaseString.hasPrefix(lowercaseKeyword)
})
if (searchResults.count == 0) {
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.name.lowercaseString.hasPrefix(lowercaseKeyword)
})
}
searchResults.sortInPlace {
($0.ticker as String) < ($1.ticker as String)
}
return searchResults;
}
Search on Background Thread:
public func searchStocksByKeyword(keyword:String, completion:(stocks:[SNStock])->()) {
let qualityOfServiceClass = QOS_CLASS_USER_INTERACTIVE
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
let stocks:[SNStock] = self.searchStocksByKeyword(keyword)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(stocks: stocks)
})
})
}

Resources