Filtering out punctuation marks - swift

struct Mirror {
var size: Int
var name: String
}
let mirrors = [(name: "Dorian", size: 24), (name: "Corinth", size: 33), (name: "Cruyf", size: 29), (name: "Lola", size: 46)]
let smallMirrors = mirrors.filter {$0.size < 33 }
print(smallMirrors)
The problem here is that the result in Playgrounds is ["Dorian", 24 "Cruyf, 29"] - all marks included.
I want a list
Dorian 24
Cruyf 29
no "" or commas

You can just iterate with tuples through the smallMirrors:
for (k,v) in smallMirrors{
print(k, terminator: " ")
print(v)
}
Or an even better solution, using maps:
smallMirrors.map { (k,v) in
print(k, v)
}
Output:
Dorian 24
Cruyf 29
Hope it helps!

If you want no quotation marks and everything on the same line, you can use
import UIKit
struct Mirror {
var size: Int
var name: String
}
let mirrors = [(name: "Dorian", size: 24), (name: "Corinth", size: 33), (name: "Cruyf", size: 29), (name: "Lola", size: 46)]
let smallMirrors = mirrors.filter {$0.size < 33 }
smallMirrors.forEach{ print("\($0) \($1)", terminator:" " )}
Output:
Dorian 24 Cruyf 29
By modifying the "terminator" property of print, you can divide entries however you want. If you decide you do want each entry on a new line, just remove the terminator entirely. By taking this functional approach to the print statement, you have the flexibility to add any separators, punctuation, or other strings you may want in the future without issue.

Related

Loop through Struct to check a value

PHP programer here struggling with Swift.
How do I create a Struct (Mutidimentional array in PHP) and walk through the elements to check a value?
Here is the code I am trying but it fails:
struct Alert: Codable {
let begin: Double
let end: Double
let color: String
let message: String
}
var alertStack = [ Int: Alert ]()
alertStack[60] = Alert(begin: 60.0,
end: 55.0,
color: "green",
message: "60 Seconds" )
alertStack[30] = Alert(begin: 30.0,
end: 25.0,
color: "yellow",
message: "30 Seconds!")
var alrtColor = "default" // Set default
var alrtText = "" // Set default
for alrt in alertStack {
if alrt.begin <= secondsLeft {
alrtColor = alrt.color // <-- Error
alrtText = alrt.message
}
}
Error is "Value of tuple type 'Dictionary<Int, Alert>.Element' (aka '(key: Int, value: Alert)') has no member 'begin'"
For a PHP guy this error message is confusing. I tried a few other things but can't seem to get the result I am looking for. I am hoping there is a simple fix or example that would work.
You can enumerate each key value pair in a dictionary like so:
for (i, alrt) in alertStack
Where "i" would be your int value
But: it may be better to try to find a more Swifty way of expressing your problem (what are you trying to do?) rather than trying to translate from PHP. For example, perhaps like:
let alerts: [Alert] = [
(60, 55, "green", "60 Seconds"),
(30, 25, "yellow", "30 Seconds!")
]
.map(Alert.init(begin:end:color:message:))
let color: String
let alertText: String
if let foundAlert = alerts.last(where: { $0.begin < secondsLeft }) {
color = foundAlert.color
alertText = foundAlert.message
}
else {
color = "default"
alertText = ""
}
(Maybe there is a reason but I don't know why you would want to have them in a dictionary keyed by their begin numbers)
If it helps I would imagine your problem may be expressed something like this:
struct Alert: Codable {
let color: String
let message: String
static let defaultAlert = Alert(color: "default", message: "")
static let thresholds: [ClosedRange<Double>: Alert] = [
55...60: Alert(color: "green", message: "60 Seconds"),
25...30: Alert(color: "yellow", message: "30 Seconds!")
]
}
func currentAlert(value: Double) -> Alert {
// Return the alert where the range of seconds contains
// the provided remaining seconds value
guard let found = Alert.thresholds.first(where: {
$0.key.contains(value)
}) else {
return .defaultAlert
}
return found.value
}
print(currentAlert(value: 57)) // 60 Seconds
print(currentAlert(value: 42)) // default
print(currentAlert(value: 26)) // 30 Seconds!
You're right, that's not a big deal. Everything you need to do is write alrt.value.begin, alrt.value.color and alrt.value.message. That's because of alertStack is Dictionary<Int, Alert> (or [Int: Alert], it's the same) type. And alrt is element of Dictionary that always has key and value properties. In your case key is Int, value is Alert
for alrt in alertStack {
if alrt.value.begin <= secondsLeft {
alrtColor = alrt.value.color
alrtText = alrt.value.message
}
}

Compiler hangs indefinitely while compiling multiple files - Swift 5.5 Release

As the question states, compiling and running the 2 files below makes the compiler hang indefinitely. Here are the CLI commands I have tried:
swiftc *.swift -o combined && ./combined and cat *.swift | swift -.
The only 2 swift files in the directory are main.swift and Vehicle.swift.
I have tried compiling and running both files separately, Vehicle.class has no errors and compiles properly, main.swift has a error: cannot find 'Vehicle' in scope error, which is understandable since I need to compile them together. There is no other feedback from the compiler. What can cause the compiler to hang?
Code:
main.swift
// Stub program to demonstrate the Vehicle class
let vehicle1 = Vehicle(newNumOfDoors: 4, newMaxSpeed: 150,
newLicensePlate: "ASDF404", newColour: "Red")
vehicle1.licensePlate = "FGHJ968"
vehicle1.colour = "Green"
vehicle1.accelerate(accelerateBy: 60)
print("\nLicense Plate: " + vehicle1.licensePlate
+ "\nColour: " + vehicle1.colour
+ "\nNumber of Doors: " + vehicle1.numOfDoors
+ "\nMax Speed: " + vehicle1.maxSpeed
+ "\nCurrent Speed: " + vehicle1.speed)
Vehicle.swift
public class Vehicle {
// Properties Declaration
let numOfDoors: Int
let maxSpeed: Int
private(set) var speed: Int
var licensePlate: String
var colour: String
// Initializes a Vehicle
init (newNumOfDoors: Int, newMaxSpeed: Int,
newLicensePlate: String, newColour: String) {
self.numOfDoors = newNumOfDoors
self.licensePlate = newLicensePlate
self.maxSpeed = newMaxSpeed
self.colour = newColour
self.speed = 0
}
func accelerate(accelerateBy: Int) {
self.speed += accelerateBy
}
func brake(brakeBy: Int) {
self.speed -= brakeBy
}
}
The compiler is having issues with your print statement (which I determined by removing elements from the program until it worked). This is likely because it's struggling to figure out the type when using + to concatenate everything.
One option is to use a multi-line string literal:
let vehicle1 = Vehicle(newNumOfDoors: 4, newMaxSpeed: 150,
newLicensePlate: "ASDF404", newColour: "Red")
vehicle1.licensePlate = "FGHJ968"
vehicle1.colour = "Green"
vehicle1.accelerate(accelerateBy: 60)
let str = """
License Plate: \(vehicle1.licensePlate)
Colour: \(vehicle1.colour)
Number of Doors: \(vehicle1.numOfDoors)
Max Speed: \(vehicle1.maxSpeed)
Current Speed: \(vehicle1.speed)
"""
print(str)
Another option is to use interpolation rather than the +:
print("\nLicense Plate: \(vehicle1.licensePlate)"
+ "\nColour: \(vehicle1.colour)"
+ "\nNumber of Doors: \(vehicle1.numOfDoors)"
+ "\nMax Speed: \(vehicle1.maxSpeed)"
+ "\nCurrent Speed: \(vehicle1.speed)")

Swift function not working inside another function

I'm new on this site but I've been struggling for several days about this issue I found. I wrote this code in order to solve a challenge of the site Codewars; the challenge consists in calculate the mean and the variance from some data about some fictional rainfalls (I attach the complete page on the bottom). In order to end this challenge I created a function to convert the data from this useless string into an array of Doubles. The weird thing is that the function if called outside the main one works properly but inside returns an empty array. I have no idea why is happening this. Thank you very much for every effort you'll put trying to explain me this.
This is the first part of the Codewars page that explain the callenge
This is the second one
//
// main.swift
// Prova
//
// Created by Lorenzo Santini on 13/06/18.
// Copyright © 2018 Lorenzo Santini. All rights reserved.
//
import Foundation
func mean(_ d: String,_ town: String) -> Double {
let arrayOfValues = obtainArrayOfMeasures(d, town)
var sum: Double = 0
for element in arrayOfValues {
sum += element
}
return sum / Double(arrayOfValues.count)
}
func variance(_ d: String,_ town: String) -> Double {
let meanValue: Double = mean(d, town)
//Here is the problem: when this function is called instead of returning the array containg all the measures for the selected city it returns an empty array
var arrayOfValues = obtainArrayOfMeasures(d, town)
var sum: Double = 0
for element in arrayOfValues {
sum += pow((element - meanValue), 2)
}
return sum / Double(arrayOfValues.count)
}
func isInt(_ char: Character) -> Bool {
switch char {
case "1","2","3","4","5","6","7","8","9":
return true
default:
return false
}
}
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double]{
//The first array stores the Data string divided for city
var arrayOfString: [String] = []
//The second array stores the measures of rainfall of the town passed as argument for the function
var arrayOfMeasures: [Double] = []
//Split the d variable containg the data string in separated strings for each town and add it to the arrayOfString array
repeat {
let finalIndex = (data.index(of:"\n")) ?? data.endIndex
arrayOfString.append(String(data[data.startIndex..<finalIndex]))
let finalIndexToRemove = (data.endIndex == finalIndex) ? finalIndex : data.index(finalIndex, offsetBy: 1)
data.removeSubrange(data.startIndex..<finalIndexToRemove)
} while data.count != 0
//Find the string of the town passed as argument
var stringContainingTown: String? = nil
for string in arrayOfString {
if string.contains(town) {stringContainingTown = string; print("true")}
}
if stringContainingTown != nil {
var stringNumber = ""
var index = 0
//Add to arrayOfMeasures the measures of the selected town
for char in stringContainingTown! {
index += 1
if isInt(char) || char == "." {
stringNumber += String(char)
print(stringNumber)
}
if char == "," || index == stringContainingTown!.count {
arrayOfMeasures.append((stringNumber as NSString).doubleValue)
stringNumber = ""
}
}
}
return arrayOfMeasures
}
var data = "Rome:Jan 81.2,Feb 63.2,Mar 70.3,Apr 55.7,May 53.0,Jun 36.4,Jul 17.5,Aug 27.5,Sep 60.9,Oct 117.7,Nov 111.0,Dec 97.9" + "\n" +
"London:Jan 48.0,Feb 38.9,Mar 39.9,Apr 42.2,May 47.3,Jun 52.1,Jul 59.5,Aug 57.2,Sep 55.4,Oct 62.0,Nov 59.0,Dec 52.9" + "\n" +
"Paris:Jan 182.3,Feb 120.6,Mar 158.1,Apr 204.9,May 323.1,Jun 300.5,Jul 236.8,Aug 192.9,Sep 66.3,Oct 63.3,Nov 83.2,Dec 154.7" + "\n" +
"NY:Jan 108.7,Feb 101.8,Mar 131.9,Apr 93.5,May 98.8,Jun 93.6,Jul 102.2,Aug 131.8,Sep 92.0,Oct 82.3,Nov 107.8,Dec 94.2" + "\n" +
"Vancouver:Jan 145.7,Feb 121.4,Mar 102.3,Apr 69.2,May 55.8,Jun 47.1,Jul 31.3,Aug 37.0,Sep 59.6,Oct 116.3,Nov 154.6,Dec 171.5" + "\n" +
"Sydney:Jan 103.4,Feb 111.0,Mar 131.3,Apr 129.7,May 123.0,Jun 129.2,Jul 102.8,Aug 80.3,Sep 69.3,Oct 82.6,Nov 81.4,Dec 78.2" + "\n" +
"Bangkok:Jan 10.6,Feb 28.2,Mar 30.7,Apr 71.8,May 189.4,Jun 151.7,Jul 158.2,Aug 187.0,Sep 319.9,Oct 230.8,Nov 57.3,Dec 9.4" + "\n" +
"Tokyo:Jan 49.9,Feb 71.5,Mar 106.4,Apr 129.2,May 144.0,Jun 176.0,Jul 135.6,Aug 148.5,Sep 216.4,Oct 194.1,Nov 95.6,Dec 54.4" + "\n" +
"Beijing:Jan 3.9,Feb 4.7,Mar 8.2,Apr 18.4,May 33.0,Jun 78.1,Jul 224.3,Aug 170.0,Sep 58.4,Oct 18.0,Nov 9.3,Dec 2.7" + "\n" +
"Lima:Jan 1.2,Feb 0.9,Mar 0.7,Apr 0.4,May 0.6,Jun 1.8,Jul 4.4,Aug 3.1,Sep 3.3,Oct 1.7,Nov 0.5,Dec 0.7"
var prova = variance(data, "London")
The problem is that func obtainArrayOfMeasures modifies the global data
variable. When called the second time, data is an empty string.
An indicator for this problem is also that making the global data variable constant
let data = "Rome:..."
causes a compiler error at
data.removeSubrange(data.startIndex..<finalIndexToRemove)
// Cannot use mutating member on immutable value: 'data' is a 'let' constant
An immediate fix would be to operate on a local mutable copy:
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double]{
var data = d
// ...
}
Note however that the function can be simplified to
func obtainArrayOfMeasures(_ d: String,_ town: String) -> [Double] {
let lines = d.components(separatedBy: .newlines)
guard let line = lines.first(where: { $0.hasPrefix(town)}) else {
return [] // No matching line found.
}
let entries = line.components(separatedBy: ",")
let numbers = entries.compactMap { Double($0.filter {".0123456789".contains($0) })}
return numbers
}
without mutating any values. You might also consider to return nil
or abort with fatalError() if no matching entry is found.

Counting number of instances inside a struct in swift

I'm very new to Swift and I can't figure this one out. I need to count the number of instances created inside a struct. Since I created 3 instances, how can I get the program to tell me there are three? I tried the exNames.count at the end, but that doesn't work... Thanks!
struct People {
let name: String
var age: Int
let sex: Character
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
let exNames = [People]()
exNames.count
You want to use a static variable on the People struct. However, this does require overriding the default initializer.
struct People
{
static var instances = 0
let name:String
var age:Int
let sex:Character
init(name:String, age:Int, sex:Character)
{
self.name = name
self.age = age
self.sex = sex
People.instances += 1
}
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
let exNames = [People]()
/* exNames.count only gives the number of People that are
contained in this particular array, which is zero. */
print(People.instances) // 3
If you want to decrement the count when the structs go out of scope, you need to upgrade to a class which provides a deinitializer deinit {}.
Note that the “proper” use cases for a static counter are exceedingly limited. It is very likely that the problem you are actually trying to solve would be better served by a different hammer.
By the way, you really shouldn’t be using Character to represent sex, as Character in Swift is very closely tied to strings, and so they are built and optimized for lexical purposes, not for flagging. It also opens the door for a lot of potential bugs, as Swift won’t be able to verify valid input as well (what if someone accidentally passes a sex value of "#"?) Instead, use the built in Bool type, or a custom enum if you need more functionality.
Looks like you wanted to create an array of people, in that case:
struct People {
let name: String
var age: Int
let sex: Character
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
//This should be a var, because you are going to modify it
var exNames = [People]()
exNames.append(heather)
exNames.append(peter)
exNames.append(scott)
exNames.count

How to find matches of certain properties in a set of struct instances?

I'm experimenting with structs in Swift 3 and trying to extract/print some values. Here's my code:
struct person {
var name: String
var gender: String
var age: Int
}
var people = [
person(
name: "Steve",
gender: "Male",
age: 25
),
person(
name: "Paul",
gender: "Male",
age: 30
),
person(
name: "Sandra",
gender: "Female",
age: 21
),
person(
name: "Becky",
gender: "Female",
age: 30
),
person(
name: "Trevor",
gender: "Male",
age: 45
)
]
I've learned that I can find the age of a person if I supply the name:
let nameToSearch = "Trevor"
if let i = people.index(where: {$0.name == nameToSearch}) {
print("\(nameToSearch) is aged \(people[i].age)") // Trevor is aged 45
}
What is the best way to...
1) Capture the four oldest people in the group, sort them into descending order by age, and print them like this?
Trevor is aged 45
Paul is aged 30
Becky is aged 30
Steve is aged 25
2) Capture all instances where the gender is "Male", and print them like this?
The males are Steve, Paul, Trevor
And out of interest, assuming that the user was performing actions that led to getting/setting existing instances, as well as adding/removing instances, is there a rough number of person instances in the people array that would cause app performance to take a hit?
A succinct way to get the 4 oldest people:
let oldestPeople = people.sorted(by: { $0.0.age > $0.1.age })[0 ..< 4]
for person in oldestPeople {
print("\(person.name) is aged \(person.age)")
}
And to get the male names:
let maleNames = people.filter({ $0.gender == "Male" }).map({ $0.name })
print("The males are " + maleNames.joined(separator: ", "))
Thank you for steering me in the right direction Hamish. Here are my solutions. To get the four oldest people:
var personAndAge = [String: Int]()
people.forEach {
personAndAge[$0.name] = $0.age
}
var increment = 0
let limit = 4
for (person, age) in personAndAge.sorted(by: { $0.1 > $1.1 }) {
increment = increment + 1
if (increment <= limit) {
print("\(person) is aged \(age)")
}
}
/*
prints:
Trevor is aged 45
Becky is aged 30
Paul is aged 30
Steve is aged 25
*/
To get the males only:
let peopleFilteredToMales = people.filter { $0.gender == "Male"}
var maleNames: [String] = []
peopleFilteredToMales.forEach { maleNames.append($0.name) }
let outputString = "The males are " + maleNames.joined(separator: ", ")
print(outputString)
// prints: The males are Steve, Paul, Trevor