How to get this to output with just one Swift print statement - swift

This code produces the specific output but not with only one statement.
import Cocoa
var myTeams = ["Titans": ["Bob","Joe","Billy","Dan","Jill"], "Hawks": ["Frank","John","Matthew","Eric","Jack"], "Eagles": ["Swen","Olie","Helga","Errin","Gloggler"], "Falsons": ["Clyde","Dale","Elden","Fred","Hank","Ike","Jake"]]
myTeams.count
for teams in myTeams.keys {
let matchTeam = teams //Current team being processed
let players = myTeams[matchTeam] //Players in current team
_ = Array(arrayLiteral: players) //Create an array of players
print("\(teams) members: ")
for i in players! { // Use ! to remove optional
print(i)
}
print(" \n ")
}

You can use reduce to build one string of the whole dictionary
print(myTeams.reduce(into: "") {
$0.append("\($1.0) members:\n\($1.1.joined(separator:"\n"))\n \n") })

struct Team: CustomStringConvertible {
let name: String
let players: [String]
var description: String {
name + " members: \n" + players.flatMap{ $0 + "\n"}
}
}
var myTeams = [Team(name: "Titans", players: ["Bob","Joe","Billy","Dan","Jill"]),
Team(name: "Hawks", players: ["Frank","John","Matthew","Eric","Jack"])]
myTeams.forEach { print($0) }
I would go for struct and utilize CustomStringConvertible to achieve one line goodness. Cheers !

Related

how to get array of contact from [(String?, [Contact])]()?

I have var contacts = [(String?, [Contact])]() I need to extract all contact arrays to put it in var filteredContacts = [Contact]() to be use in search logic.
updatee the Account and Accounts object
//Account
struct Account: Codable {
var id, displayName:String
var contacts: [Contact]?
...
}
//Accounts
struct Accounts: Codable {
let accounts: [Account]
...
}
switch result {
case .success(let result):
self.accounts = result.accounts
self.contacts = Array(result.accounts
.compactMap { account in account.contacts.map { (account.displayName , $0) } }.dropFirst())
self.filteredContacts = ??
You can simply use flatMap and use the second element of the tuple as the keyPath:
self.filteredContacts = contacts.flatMap(\.1) // for older swift syntax you can use `flatMap { $1 }`
A little late to the party but if you want to use accounts or result.accounts directly you can do
self.filteredContacts = Array(result.accounts.compactMap(\.contacts).joined())
use a simple for each
contacts.forEach({
self.filteredContacts.append(contentsOf: $0.1)
})

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.

Swift- how to initialize struct instance with function or other pattern

This is a dumb example, but I can't think of the right way to avoid repeating myself when I try to initialize my struct instances below. Notice how they get the same initializer (not sure if that's the right phrase), but what would be another way to do this so i'm giving it a function or something like that instead of the same struct.init(...)?
struct InnerSt {
var a: String
var b: String
}
var myStructs: [InnerSt] = []
func assignVal() {
for item in ["dog", "cat", "fish"] {
let a: String = "I'm a"
var pets: String
let inner: InnerSt = InnerSt.init(a: a, b: item)
switch item {
case "dog":
pets = "hairy"
//print(inner.a + " " + inner.b + " and I'm " + pets) //this is like what I want to repeatedly do without the repetition
myStructs.append(inner) //this works nicely but obviously I miss adding the pets variable
case "cat":
pets = "furry"
//print(inner.a + " " + inner.b + " and I'm " + pets)
myStructs.append(inner)
case "fish":
pets = "scaly"
//print(inner.a + " " + inner.b + " and I'm " + pets)
myStructs.append(inner)
default: ()
}
}
}
assignVal()
print(myStructs)
To avoid writing a bunch of initialisers you could simply change your implementation as follows:
func assignVal() {
let a = "I'm a "
for item in [1, 2] {
let temp = InnerSt.init(a: a, b: item)
print(temp)
}
}
Basically, you do not need to switch because item is being assigned as you loop. It will be assigned the value of 1 on the first iteration and 2 on the second.
The benefits are:
The InnerSt initialiser is written once (even though it is called multiple times).
If your array [1, 2] grows (to say [1, 2, 3]) you would not need to add new case to your switch.
A few side notes that helped me in the beginning:
InnerSt.init(a: a, b: item) can be shortened to InnerSt(a: a, b: item). Nice for readability.
let a: String = "I'm a" can be shorted to let a = "I'm a". Swift has an excellent type inference system. In this case the complier will infer that a is of type String.
innserSt would be better named InnerSt. See Apple's excellent guidelines.
Revision after comments
Playground code:
var petsDescriptions: [String] = [] // array of string descriptions of the animals
var pets = ["dog", "cat", "fish", "deer"] // array of all animals
func assignVal() {
for pet in pets {
var surfaceFeeling: String = "" // variable to hold the surface feeling of the pet e.g. "hairy"
switch pet { // switch on the pet
case "dog":
surfaceFeeling = "hairy"
case "cat":
surfaceFeeling = "furry"
case "fish":
surfaceFeeling = "scaly"
default:
surfaceFeeling = "<unknown>"
}
let description = "I'm \(surfaceFeeling) and I'm a \(pet)" // construct the string description
petsDescriptions.append(description) // add it to the storage array
}
}
assignVal()
print(petsDescriptions)
Console output:
["I\'m hairy and I\'m a dog", "I\'m furry and I\'m a cat", "I\'m scaly and I\'m a fish", "I\'m <unknown> and I\'m a deer"]
Let me know if I answered your question correctly or need to add some more information.

Swift split string to array with exclusion

I write Swift application that parse log file.
log file string:
substr1 substr2 "substr 3" substr4
I need to get array: [substr1, substr2, substr 3, substr4]
But if I use something like:
print(stringLine.components(separatedBy: " "))
I got: [substr1, substr2, "substr, 3", substr4].
How to receive array: [substr1, substr2, substr 3, substr4]?
One of the possible solutions is to use map:
let testSting = "substr1 substr2 \"substr3\" substr4"
let mappedString = testString.components(separatedBy: " ").map({$0.replacingOccurrences(of: "\"", with: "")})
print(mappedString) //["substr1", "substr2", "substr3", "substr4"]
This case of the issue is required to use regular expression but this example is provided. So to solve problem in you're case it is possible to go in this way:
var testStingArray = testSting.replacingOccurrences(of: "\"", with: "").components(separatedBy: " ")
var arr = [String]()
var step = 0
while step < testStingArray.count {
var current = testStingArray[step]
var next = step + 1
if next < testStingArray.count {
if testStingArray[next].characters.count == 1 {
current += " " + testStingArray[next]
testStingArray.remove(at: next)
}
}
arr.append(current)
step += 1
}
print(arr)//["substr1", "substr2", "substr 3", "substr4"]
You'd better work with regular expression:
let pattern = "([^\\s\"]+|\"[^\"]+\")"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let line = "substr1 substr2 \"substr 3\" substr4"
let arr = regex.matches(in: line, options: [], range: NSRange(0..<line.utf16.count))
.map{(line as NSString).substring(with: $0.rangeAt(1)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))}
print(arr) //->["substr1", "substr2", "substr 3", "substr4"]
Alternatively you could split the string based on a CharacterSet and then filter out the empty occurrences:
let stringLine = "substr1 substr2 \"substr3\" substr4"
let array = stringLine.components(separatedBy: CharacterSet(charactersIn: "\" ")).filter { !$0.isEmpty }
print (array)
Output: ["substr1", "substr2", "substr3", "substr4"]
But this will not work correctly if there is a " somewhere in one of the 'substrings', then that specific substring will also be split.
Or, simply iterate over the characters and maintain state about the quoted parts:
//: Playground - noun: a place where people can play
import UIKit
extension String {
func parse() -> [String] {
let delimiter = Character(" ")
let quote = Character("\"")
var tokens = [String]()
var pending = ""
var isQuoted = false
for character in self.characters {
if character == quote {
isQuoted = !isQuoted
}
else if character == delimiter && !isQuoted {
tokens.append(pending)
pending = ""
}
else {
pending.append(character)
}
}
// Add final token
if !pending.isEmpty {
tokens.append(pending)
}
return tokens
}
}
print ("substr1 substr2 \"substr 3\" substr4".parse()) // ["substr1", "substr2", "substr 3", "substr4"]
print ("\"substr 1\" substr2 \"substr 3\" substr4".parse()) // ["substr 1", "substr2", "substr 3", "substr4"]
print ("a b c d".parse()) // ["a", "b", "c", "d"]
Note: this code doesn't take into account that double quotes "" might be used to escape a single quote. But I don't know if that's a possibility in your case.
https://tburette.github.io/blog/2014/05/25/so-you-want-to-write-your-own-CSV-code/

Join strings in a block

I have a block that updates the view for each String. In its object class I pass it by:
func eachFeaturesSection(block: ((String?) -> Void)?) {
propertyFeatures.forEach { feature in
guard let feature = feature as? RealmString else {
return
}
let features = feature.stringValue
block?(features)
}
}
and I will get it in ViewController, by:
listing!.eachFeaturesSection({ (features) in
print(features)
self.facilities = features!
})
So it will print as:
Optional("String 1")
Optional("String 2")
and self.facilities will be set to latest value which is self.facilities = "String 2"
cell.features.text = features // it will print String 2
So, how can I achieve to join all strings together in one string such as self.facilities = "String 1, String 2". I used .jointString does not work. Thank you for any help.
Maybe you could add them to an array of String elements and then, when done, call joined on that array.
So something like this in your ViewController:
var featuresArray = [String]()
listing!.eachFeaturesSectionT({ (features) in
print(features)
featuresArray.append(features!)
})
//Swift 3 syntax
cell.features.text = featuresArray.joined(separator: ", ")
//Swift 2 syntax
cell.features.text = featuresArray.joinWithSeparator(", ")
Hope that helps you.
self.facilities = features! is doing nothing but keeps updating the value every iteration
Change the line self.facilities = features! to self.facilities += features! or self.facilities = self.facilities + ", " + features!
Here's how I'd do it (assuming your propertyFeatures is an array of RealmString):
Swift 3:
let string = (propertyFeatures.map { $0.stringValue }).joined(separator: ", ")
Swift 2:
let string = (propertyFeatures.map { $0.stringValue }).joinWithSeparator(", ")